簡體   English   中英

線程安全隊列有問題嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM