[英]Problem with thread-safe queue?
我正在嘗試使用c ++中的pthreads編寫一個線程安全的隊列。 我的程序有93%的時間都在工作。 另外7%的時間它會吐出垃圾,或者似乎睡着了。 我想知道我的隊列中是否存在一些上下文切換會破壞它的缺陷?
// thread-safe queue
// inspired by http://msmvps.com/blogs/vandooren/archive/2007/01/05/creating-a-thread-safe-producer-consumer-queue-in-c-without-using-locks.aspx
// only works with one producer and one consumer
#include <pthread.h>
#include <exception>
template<class T>
class tsqueue
{
private:
volatile int m_ReadIndex, m_WriteIndex;
volatile T *m_Data;
volatile bool m_Done;
const int m_Size;
pthread_mutex_t m_ReadMutex, m_WriteMutex;
pthread_cond_t m_ReadCond, m_WriteCond;
public:
tsqueue(const int &size);
~tsqueue();
void push(const T &elem);
T pop();
void terminate();
bool isDone() const;
};
template <class T>
tsqueue<T>::tsqueue(const int &size) : m_ReadIndex(0), m_WriteIndex(0), m_Size(size), m_Done(false) {
m_Data = new T[size];
pthread_mutex_init(&m_ReadMutex, NULL);
pthread_mutex_init(&m_WriteMutex, NULL);
pthread_cond_init(&m_WriteCond, NULL);
pthread_cond_init(&m_WriteCond, NULL);
}
template <class T>
tsqueue<T>::~tsqueue() {
delete[] m_Data;
pthread_mutex_destroy(&m_ReadMutex);
pthread_mutex_destroy(&m_WriteMutex);
pthread_cond_destroy(&m_ReadCond);
pthread_cond_destroy(&m_WriteCond);
}
template <class T>
void tsqueue<T>::push(const T &elem) {
int next = (m_WriteIndex + 1) % m_Size;
if(next == m_ReadIndex) {
pthread_mutex_lock(&m_WriteMutex);
pthread_cond_wait(&m_WriteCond, &m_WriteMutex);
pthread_mutex_unlock(&m_WriteMutex);
}
m_Data[m_WriteIndex] = elem;
m_WriteIndex = next;
pthread_cond_signal(&m_ReadCond);
}
template <class T>
T tsqueue<T>::pop() {
if(m_ReadIndex == m_WriteIndex) {
pthread_mutex_lock(&m_ReadMutex);
pthread_cond_wait(&m_ReadCond, &m_ReadMutex);
pthread_mutex_unlock(&m_ReadMutex);
if(m_Done && m_ReadIndex == m_WriteIndex) throw "queue empty and terminated";
}
int next = (m_ReadIndex +1) % m_Size;
T elem = m_Data[m_ReadIndex];
m_ReadIndex = next;
pthread_cond_signal(&m_WriteCond);
return elem;
}
template <class T>
void tsqueue<T>::terminate() {
m_Done = true;
pthread_cond_signal(&m_ReadCond);
}
template <class T>
bool tsqueue<T>::isDone() const {
return (m_Done && m_ReadIndex == m_WriteIndex);
}
這可以這樣使用:
// thread 1
while(cin.get(c)) {
queue1.push(c);
}
queue1.terminate();
// thread 2
while(!queue1.isDone()) {
try{ c = queue1.pop(); }
catch(char const* str){break;}
cout.put(c);
}
如果有人發現這個問題,請說出來:)
是的,這里肯定存在問題。 您對隊列成員變量的所有訪問都發生在互斥鎖之外 。 事實上,我並不完全確定你的互斥鎖正在保護什么,因為它們只是在條件變量上等待。
此外,您的讀者和編寫器似乎總是以鎖定步驟運行,從不允許隊列超出一個元素的大小。
如果這是你的實際代碼,那么一個問題就是你要初始化m_WriteCond
兩次,而不是初始化m_ReadCond
。
您應該將此類視為監視器 。 您應該為每個隊列(一個普通的互斥鎖)都有一個“監視器鎖定”。 無論何時輸入讀取或寫入隊列中任何字段的方法,都應在輸入后立即鎖定此互斥鎖。 這可以防止多個線程一次與隊列交互。 您應該在等待某個條件之前以及當您離開某個方法時釋放鎖定,以便其他線程可以進入。 完成等待條件后,請務必重新獲取鎖定。
如果你想要任何性能不錯的東西,我強烈建議你轉儲你的R / W鎖,只需使用一個非常簡單的螺旋鎖。 或者,如果你真的認為你可以通過R / W鎖定獲得你想要的性能,我會根據Joe Duffy的這個設計(單字R / W Spinlock)推出自己的性能。
似乎問題是你有一個競爭條件,線程2 CAN在線程1執行任何cin.get(c)之前運行。 需要確保數據已初始化,並且當您獲得信息時,如果尚未輸入數據,則確保您正在執行某些操作。
也許這是我沒有看到代碼的其余部分,盡管如此。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.