2013-01-14 11 views
5

Ürünlerimizden biriyle kullandığımız dahili bir bellek yöneticimiz var. Bellek yöneticisi new ve delete operatörlerini geçersiz kılar ve tek iş parçacıklı uygulamalarda düzgün çalışır. Ancak, şimdi de çok iş parçacıklı uygulamalarla çalışmak için görevlendirildim. Anladığım kadarıyla aşağıdaki sözde kod çalışmalı, ancak try_lock() ile bile bir sıkışmada kalıyor. Herhangi bir fikir?std :: mutex kilidi, yeni operatörün üstünü açarken kilitleniyor

Güncelleme 1.

Nedenleri "Erişim ihlali":

#include <mutex> 

std::mutex g_mutex; 
bool g_systemInitiated = false; 


/*! 
\brief Overrides the Standard C++ new operator 
\param size [in] Number of bytes to allocate 
*/ 
void *operator new(size_t size) 
{ 
    if (g_systemInitiated == false) return malloc(size); 
    g_mutex.lock(); // Thread hangs forever here. g_mutex.try_lock() also hangs 
    ... 
} 

int main(int argc, const char* argv[]) 
{ 
    // Tell the new() operator that the system has initiated 
    g_systemInitiated = true; 
    ... 
} 

Güncelleme 2.

: saymazsınız sonsuza asmak için

#include <mutex> 

std::mutex g_mutex; 

/*! 
\brief Overrides the Standard C++ new operator 
\param size [in] Number of bytes to allocate 
*/ 
void *operator new(size_t size) 
{ 
    g_mutex.lock(); // Access violation exception 
    ... 
} 

nedenleri iplik

A özyinelemeli muteks da saymazsınız sonsuza asmak için iş parçacığı neden olur:

#include <mutex> 

std::recursive_mutex g_mutex; 
bool g_systemInitiated = false; 


/*! 
\brief Overrides the Standard C++ new operator 
\param size [in] Number of bytes to allocate 
*/ 
void *operator new(size_t size) 
{ 
    if (g_systemInitiated == false) return malloc(size); 
    g_mutex.lock(); // Thread hangs forever here. g_mutex.try_lock() also hangs 
    ... 
} 

int main(int argc, const char* argv[]) 
{ 
    // Tell the new() operator that the system has initiated 
    g_systemInitiated = true; 
    ... 
} 

Güncelleme 3. Jonathan Wakely ben unique_lock ve/veya lock_guard, denemelisiniz ama kilit hala asılı önerdi

çevirmek.

unique_lock testi:

#include <mutex> 

std::mutex g_mutex; 
std::unique_lock<std::mutex> g_lock1(g_mutex, std::defer_lock); 
bool g_systemInitiated = false; 

/*! 
\brief Overrides the Standard C++ new operator 
\param size [in] Number of bytes to allocate 
*/ 
void *operator new(size_t size) 
{ 
    if (g_systemInitiated == false) return malloc(size); 
    g_lock1.lock(); // Thread hangs forever here the first time it is called 
    ... 
} 

int main(int argc, const char* argv[]) 
{ 
    // Tell the new() operator that the system has initiated 
    g_systemInitiated = true; 
    ... 
} 

lock_guard testi:

#include <mutex> 

std::recursive_mutex g_mutex; 
bool g_systemInitiated = false; 


/*! 
\brief Overrides the Standard C++ new operator 
\param size [in] Number of bytes to allocate 
*/ 
void *operator new(size_t size) 
{ 
    if (g_systemInitiated == false) return malloc(size); 
    std::lock_guard<std::mutex> g_lock_guard1(g_mutex); // Thread hangs forever here the first time it is called 
    ... 
} 

int main(int argc, const char* argv[]) 
{ 
    // Tell the new() operator that the system has initiated 
    g_systemInitiated = true; 
    ... 
} 

benim sorun kilitlenmesi sırasında delete C++ 11 muteks kütüphaneye tarafından çağrılan olduğunu düşünüyorum. delete da şöyle geçersiz kılınır:

/*! 
\brief Overrides the Standard C++ new operator 
\param p [in] The pointer to memory to free 
*/ 
void operator delete(void *p) 
{ 
    if (g_systemInitiated == false) 
    { 
     free(p); 
    } 
    else 
    { 
     std::lock_guard<std::mutex> g_lock_guard1(g_mutex); 
     ... 
    } 
} 

Bu kilitleme veya açma sırasında new veya delete herhangi bir çağrı yumurtlamaya vermez kendi kilitleme yapmak için hariç için Her iyi çözüm göremiyorum durum kilitleyebilmenize neden olur.

Güncelleme 4. Ben new veya delete hiçbir aramaları var kendi özel özyinelemeli Muteksleri uyguladık

, ayrıca, aynı iş parçacığı kilitli blok girmek için izin verir.

#include <thread> 

std::thread::id g_lockedByThread; 
bool g_isLocked = false; 
bool g_systemInitiated = false; 

/*! 
\brief Overrides the Standard C++ new operator 
\param size [in] Number of bytes to allocate 
*/ 
void *operator new(size_t size) 
{ 
    if (g_systemInitiated == false) return malloc(size); 

    while (g_isLocked && g_lockedByThread != std::this_thread::get_id()); 
    g_isLocked = true; // Atomic operation 
    g_lockedByThread = std::this_thread::get_id(); 
    ... 
    g_isLocked = false; 
} 

/*! 
\brief Overrides the Standard C++ new operator 
\param p [in] The pointer to memory to free 
*/ 
void operator delete(void *p) 
{ 
    if (g_systemInitiated == false) 
    { 
     free(p); 
    } 
    else 
    { 
     while (g_isLocked && g_lockedByThread != std::this_thread::get_id()); 
     g_isLocked = true; // Atomic operation 
     g_lockedByThread = std::this_thread::get_id(); 
     ... 
     g_isLocked = false; 
    } 
} 

int main(int argc, const char* argv[]) 
{ 
    // Tell the new() operator that the system has initiated 
    g_systemInitiated = true; 
    ... 
} 

Güncelleme 5.

çalıştı Jonathan Wakely öneri ve kesinlikle C++ 11 muteksleri Microsoft'un uygulama ile ilgili bir sorun olduğu görülmektedir bulundu ; onun örneği, /MTd (Çok iş parçacıklı Debug) derleyici bayrağıyla derlenmişse askıda kalıyor, ancak /MDd (Çok iş parçacıklı Debug DLL) derleyici bayrağıyla derlenmişse iyi çalışıyor. Jonathan'ın doğru bir şekilde işaret ettiği gibi std::mutex uygulamalarının constexpr olması gerekiyor.

#include "stdafx.h" 

#include <mutex> 
#include <iostream> 

bool g_systemInitiated = false; 
std::mutex g_mutex; 

void *operator new(size_t size) 
{ 
    if (g_systemInitiated == false) return malloc(size); 
    std::lock_guard<std::mutex> lock(g_mutex); 
    std::cout << "Inside new() critical section" << std::endl; 
    // <-- Memory manager would be called here, dummy call to malloc() in stead 
    return malloc(size); 
} 

void operator delete(void *p) 
{ 
    if (g_systemInitiated == false) free(p); 
    else 
    { 
     std::lock_guard<std::mutex> lock(g_mutex); 
     std::cout << "Inside delete() critical section" << std::endl; 
     // <-- Memory manager would be called here, dummy call to free() in stead 
     free(p); 
    } 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    g_systemInitiated = true; 

    char *test = new char[100]; 
    std::cout << "Allocated" << std::endl; 
    delete test; 
    std::cout << "Deleted" << std::endl; 

    return 0; 
} 

Güncelleme # Microsoft'a bir hata raporu Ekleyen 6

: Burada uygulama sorunu sınamak için kullanılan VS 2012 C++ kodu https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag#details

+1

bunu bağlayabilirsiniz nasıl görmüyorum, nereye senin g_mutex tanımlıyorsunuz? iki deklarasyon var, bunlardan birinin ön tarafına sahip olmalısınız. –

+0

Yeni ve silme işleçleriniz gerçekten ne yapıyorsunuz (bellek ayırma/boşaltma dışında)? –

+0

@claptrap Bu sahte koddur. –

cevap

1

muteks kütüphanesi new kullanır, ve std :: mutex, varsayılan olarak özyinelemeli (yani tekrarlayan) değildir. Tavuk ve yumurta problemi.

UPDATE Aşağıdaki yorumlarda belirtildiği gibi, std :: recursive_mutex kullanılması işe yarayabilir. Ama iyi tanımlanmış kalıntıları olmanın globalsin statik başlatma sırasına klasik C++ sorunu, küresel Muteksleri dışarıdan erişime tehlikesini (anonim ad içine koymak en iyisidir.)

GÜNCELLEME yaptığı gibi 2 You g_systemInitiated öğesinin doğru olması için çok erken olabilir, yani mutex başlatma işlemini tamamlamadan önce ve "ilk defaya mahsus" çağrıları malloc() asla gerçekleşmez. Bu zorlamak için, ayırıcı modülünde bir başlatma işlevine bir çağrı ile) (ana atamayı değiştirme deneyebilirsiniz:

namespace { 
    std::recursive_mutex g_mutex; 
    bool     g_initialized = false; 
} 
void initialize() 
{ 
    g_mutex.lock(); 
    g_initialized = true; 
    g_mutex.unlock(); 
} 
+0

Tamam. Yani, bunu atlatmanın bir yolu var mı? Bir geçersiz 'yeni' operatör kilitlemek için bir yol? –

+0

Std :: mutex için bildiğimden değil. Ancak, alt düzey pthreads kitaplığı ile, (statik olarak) global pthreads_mutex_t dosyasını PTHREAD_RECURSIVE_MUTEX_INITIALIZER ile sorun haline getirebilirsiniz. Ancak tabiki std :: thread API ile kodlama rahatlığını kaybedersiniz. – arayq2

+0

iyi o yerine std :: recursive_mutex yerine –

0

senin örneklerin hepsi sadece çok kötü ilki hariç kırık Kapsamlı bir kilit tipi kullanmamak için pratik yapın.

değil sizin derleyici yapar veya standart kütüphane kırık ise bu çalışması gerekir:

#include <mutex> 

std::mutex g_mutex; 

void *operator new(size_t size) 
{ 
    std::lock_guard<std::mutex> lock(g_mutex); 
    ... 
} 
+0

Kesinlikle Microsoft'un C++ 11 Mutexes uygulamasıyla ilgili bir sorun var gibi görünüyor; örneğiniz hanch id '/ MTd' (Çok iş parçacıklı Debug) derleyici bayrağıyla derler, ancak'/MDd' (Çok iş parçacıklı Debug DLL) derleyici bayrağıyla derlersek çalışır. Soruma bir güncelleme ekleyeceğim. –