2013-06-21 44 views
2

我正在編程併發隊列,同時學習如何使用C++ 11的多線程功能。 當消費者調用dequeue()函數並且隊列沒有條目時,該函數應該等到另一個線程調用enqueue()。我爲此使用了condition_variable。 我的測試工作正常有幾個條目和線程,但是當我使用多個(最多100000元,20和生產者,只有1個用戶),我得到的condition_variable::wait函數內部訪問衝突:std :: condition_variable :: wait訪問衝突

Unbehandelte Ausnahme貝0x5A2C7EEC(msvcr110d.dll)在Tests.exe: 0000005:Zugriffsverletzung BEIM Lesen的位置0xFEEEFEF6

我已經堅持了好幾個小時了。我希望你能幫助我。謝謝。

的代碼:

// -------------------------------------------------------------------------------------- 
// Concurrent Queue 
// -------------------------------------------------------------------------------------- 

#pragma once 
#include <atomic> // Atomic operations for lock-free operations 
#include <mutex> 
#include <condition_variable> 
using namespace std; 


// -------------------------------------------------------------------------------------- 
// Declarations 
// -------------------------------------------------------------------------------------- 

template<typename T> 
class ConcurrentQueue; 

template<typename T> 
class ConcurrentQueueEntry; 


// -------------------------------------------------------------------------------------- 
// Queue 
// -------------------------------------------------------------------------------------- 

template<typename T> 
class ConcurrentQueue { 
public: 
    ConcurrentQueue(); 
    ~ConcurrentQueue(); 

    void enqueue(const T value); 
    T try_dequeue(); 
    T dequeue(); 

    unsigned long count() const; 

private: 
    atomic<ConcurrentQueueEntry<T>*> front; 
    atomic<ConcurrentQueueEntry<T>*> rear; 

    atomic_ulong i_count; 

    mutex dequeueWaitMutex; 
    condition_variable dequeueWaitCV; 
}; 


// -------------------------------------------------------------------------------------- 
// Entry 
// -------------------------------------------------------------------------------------- 

template<typename T> 
class ConcurrentQueueEntry { 
public: 
    ConcurrentQueueEntry(T _value); 

    T value; 
    atomic<ConcurrentQueueEntry<T>*> next; 
}; 


// -------------------------------------------------------------------------------------- 
// Exception: Queue is empty 
// -------------------------------------------------------------------------------------- 

class EmptyQueueException {}; 


// -------------------------------------------------------------------------------------- 
// Constructors and Destructor 
// -------------------------------------------------------------------------------------- 

// Create Queue 
template<typename T> 
ConcurrentQueue<T>::ConcurrentQueue() 
    : front(), rear(), i_count(), dequeueWaitMutex(), dequeueWaitCV() 
{ 
    i_count.store(0); 
} 

// Delete Queue 
template<typename T> 
ConcurrentQueue<T>::~ConcurrentQueue() 
{ 
    ConcurrentQueueEntry<T>* previous = this->front.load(); 
    while(previous != NULL) { 
     ConcurrentQueueEntry<T>* next = previous->next.load(); 
     delete previous; 
     previous = next; 
    } 
} 

// Create Entry 
template<typename T> 
ConcurrentQueueEntry<T>::ConcurrentQueueEntry 
    (T _value) 
    : value(_value), next(NULL) 
{ } 


// -------------------------------------------------------------------------------------- 
// Public Methods 
// -------------------------------------------------------------------------------------- 

// Enqueue 
template<typename T> 
void ConcurrentQueue<T>::enqueue 
    (const T value) 
{ 
    // create, append 
    ConcurrentQueueEntry<T>* entry = new ConcurrentQueueEntry<T>(value); 
    ConcurrentQueueEntry<T>* former_rear = this->rear.exchange(entry); 

    // connect 
    if(former_rear == NULL) { 
     this->front.store(entry); 
    } 
    else { 
     former_rear->next.store(entry); 
    } 

    // Add 
    ++i_count; 
    dequeueWaitCV.notify_one(); 
} 

// Dequeue (aborts if queue is empty) 
template<typename T> 
T ConcurrentQueue<T>::try_dequeue() 
{ 
    ConcurrentQueueEntry<T>* front = this->front.load(); 
    while(front != NULL && 
     !this->front.compare_exchange_weak(front, front->next.load())); 

    if(front == NULL) 
     throw EmptyQueueException(); 

    --i_count; 
    T value = front->value; 
    delete front; 
    return value; 
} 

// Dequeue (waits if queue is empty) 
template<typename T> 
T ConcurrentQueue<T>::dequeue() { 
    while(true) { 
     try { 
      return this->try_dequeue(); 
     } 
     catch(EmptyQueueException) { 
      unique_lock<mutex> lock(dequeueWaitMutex); 
      dequeueWaitCV.wait(lock, [&] { return this->count() == 0; }); 
     } 
    } 
} 

// Count entries 
template<typename T> 
unsigned long ConcurrentQueue<T>::count() const { 
    return this->i_count.load(); 
} 

調用堆棧:

msvcr110d.dll!Concurrency::details::LockQueueNode::IsTicketValid() Zeile 924 C++ 
msvcr110d.dll!Concurrency::details::LockQueueNode::UpdateQueuePosition(Concurrency::details::LockQueueNode * pPreviousNode) Zeile 811 C++ 
msvcr110d.dll!Concurrency::critical_section::_Acquire_lock(void * _PLockingNode, bool _FHasExternalNode) Zeile 1193 C++ 
msvcr110d.dll!Concurrency::critical_section::lock() Zeile 1028 C++ 
msvcr110d.dll!Concurrency::details::_Condition_variable::wait(Concurrency::critical_section & _Lck) Zeile 576 C++ 
msvcp110d.dll!do_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx, const xtime * target) Zeile 47 C++ 
msvcp110d.dll!_Cnd_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx) Zeile 73 C++ 
Tests.exe!std::_Cnd_waitX(_Cnd_internal_imp_t * * _Cnd, _Mtx_internal_imp_t * * _Mtx) Zeile 93 C++ 
Tests.exe!std::condition_variable::wait(std::unique_lock<std::mutex> & _Lck) Zeile 60 C++ 
Tests.exe!std::condition_variable::wait<<lambda_61c2d1dffb87d02ed418fe62879bb063> >(std::unique_lock<std::mutex> & _Lck, ConcurrentQueue<long>::dequeue::__l7::<lambda_61c2d1dffb87d02ed418fe62879bb063> _Pred) Zeile 67 C++ 
Tests.exe!ConcurrentQueue<long>::dequeue() Zeile 156 C++ 
Tests.exe!<lambda_c8c79a4136723f6fef9d0a0557ed768b>::operator()() Zeile 38 C++ 
Tests.exe!std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()() Zeile 1152 C++ 
Tests.exe!std::_LaunchPad<std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >::_Run(std::_LaunchPad<std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> > * _Ln) Zeile 196 C++ 
Tests.exe!std::_LaunchPad<std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >::_Go() Zeile 187 C++ 
msvcp110d.dll!_Call_func(void * _Data) Zeile 52 C++ 
msvcr110d.dll!_callthreadstartex() Zeile 354 C 
msvcr110d.dll!_threadstartex(void * ptd) Zeile 337 C 
kernel32.dll!747f850d() Unbekannt 
[Unten angegebene Rahmen sind möglicherweise nicht korrekt und/oder fehlen, keine Symbole geladen für kernel32.dll] 
ntdll.dll!7719bf39() Unbekannt 
ntdll.dll!7719bf0c() Unbekannt 
+0

我得到同樣的東西,當LockQueueNode :: UpdateQueuePosition被調用時看到大量的崩潰。我開始認爲在Visual Studio運行時可能存在競爭條件 – Lodle

回答

4

兩個音符前面用於調試:

  • 0xFEEEFEF6是0xFEEEFEEE + 8. 0xFEEEFEEE是信號值調試運行時存儲在一些變量中。我認爲這個值表明擁有對象的析構函數已經被調用。相反,在初始化之前,我想這個值是0xCDCDCDCD。
  • 您可以配置VS關閉國際化。這樣你會得到英文錯誤信息,這更適合這裏的論壇。

現在,關於您的代碼,第一件事情是,這不是「代碼」,而只是其中的一部分。請舉個簡單的例子。但是,與此同時,您可以使您的隊列類不可複製和不可分配(使用「.. = delete;」)。如果我猜對了,那麼你可能會遇到編譯錯誤,這些錯誤可能來自複製隊列,可能在線程啓動期間。