2013-01-13 81 views
0

我目前正在嘗試編寫併發隊列,但我有一些段錯誤,我無法向自己解釋。我的隊列實現本質上是由本網站上的第一個列表給出的。併發隊列中的競爭條件

http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

該網站說,有一個競爭條件,如果對象是並行地從隊列中刪除,但我只是不明白爲什麼還有一個,任何人都可以解釋給我嗎?

編輯:這是代碼:

template<typename Data> 
class concurrent_queue 
{ 
private: 
    std::queue<Data> the_queue; 
    mutable boost::mutex the_mutex; 
public: 
    void push(const Data& data) 
    { 
     boost::mutex::scoped_lock lock(the_mutex); 
     the_queue.push(data); 
    } 

    bool empty() const 
    { 
     boost::mutex::scoped_lock lock(the_mutex); 
     return the_queue.empty(); 
    } 

    Data& front() 
    { 
     boost::mutex::scoped_lock lock(the_mutex); 
     return the_queue.front(); 
    } 

    Data const& front() const 
    { 
     boost::mutex::scoped_lock lock(the_mutex); 
     return the_queue.front(); 
    } 

    void pop() 
    { 
     boost::mutex::scoped_lock lock(the_mutex); 
     the_queue.pop(); 
    } 
}; 
+0

那裏有一個以上的代碼片段,他們說哪一個代碼片段有比賽? – hmatar

+0

...細節在這裏非常重要。請發佈您正在使用的實際代碼。 – Mat

+0

他的意思是實現*阻塞*併發隊列。如果隊列在使用時試圖從中彈出項目,那該怎麼辦? – Nawaz

回答

0

如果使用空並找到隊列不爲空,另一個線程可能會突然出現使得空的使用結果之前的項目。

與前面類似,您可以閱讀前面的項目,並且在您使用該項目時,它可能會被另一個線程彈出。

+0

這是一個典型的錯誤,它需要將接口更改爲隊列。您可以返回空值或拋出異常。或者,您可以阻止,直到有一個值可用,但在這種情況下,除了互斥鎖之外,您還需要一個條件變量,除非您想要輪詢。 –

+0

我認爲你的解釋並不反映提問者提交的代碼。或者你想告訴我,the_mutex的範圍不包括函數調用? – hmatar

+0

它只包含函數調用,但它必須包含對'empty()'和'front()'的調用,而不是。 –

1

如果在嘗試從中讀取項目時隊列爲空,該怎麼辦?

想到這個用戶代碼:

while(!q.empty()) //here you check q is not empty 
{ 
     //since q is not empty, you enter inside the loop 
     //BUT before executing the next statement in this loop body, 
     //the OS transfers the control to the other thread 
     //which removes items from q, making it empty!! 
     //then this thread executes the following statement! 
     auto item = q.front(); //what would it do (given q is empty?) 
} 
+0

嗯,我寧願一個阻塞隊列,但我會解決一個不......我只是想從主線程放一些工作,然後使用幾個工作線程檢索和處理它們 – hfhc2

+0

@ hfhc2:現在查看我的代碼。 – Nawaz

+0

我認爲你的解釋並不反映提問者提交的代碼。或者你想告訴我,the_mutex的範圍不包括函數調用? – hmatar

0

從@parkydr和@Nawaz的答案是正確的,但這裏的另一個回味無窮;

你想達到什麼目的?

有一個線程安全隊列的原因有時(我不敢說經常)誤。在很多情況下,您希望將隊列鎖定在「外部」,在隊列只是實現細節的上下文中。然而

的原因之一,線程安全隊列是消費者 - 生產者情況,其中1-N節點推送數據,也不管他們得到了什麼1-M節點從中彈出。隊列中的所有元素都是平等的,消費者只是彈出而不知道他們得到了什麼,並開始處理數據。在這種情況下,你的界面不應該公開T& front()。那麼,如果你不確定哪裏有物品(如果沒有外部鎖定,你永遠不能確定),你永遠都不應該返回一個參考。我會推薦使用unique_ptr的(或當然是shared_ptr),並且只暴露種族自由功能(爲了簡潔起見,我將忽略const函數)。使用std::unique_ptr需要C++ 11,但你可以使用boost::shared_ptr爲相同的功能,如果C++ 11是不可能讓你使用:

// Returns the first item, or an empty unique_ptr 
std::unique_ptr<T> pop(); 

// Returns the first item if it exists. Otherwise, waits at most <timeout> for 
// a value to be be pushed. Returns an empty unique_ptr if timeout was reached. 
std::unique_ptr<T> pop({implementation-specific-type} timeout); 

void push(std::unique_ptr<T>&& ptr); 

功能,如exist()front()是比賽的受害者自然條件,因爲他們不能自動執行你(想你)想要的任務。 exist()有時會返回您收到結果時不正確的值,如果隊列爲空,則front()必須拋出。

0

我想爲什麼empty()函數是無用的/危險的答案很清楚。如果你想要一個阻塞隊列,請刪除它。

相反,添加一個條件變量(boost :: condition,IIRC)。功能推/然後在彈出如下圖所示:

void push(T data) 
{ 
    scoped_lock lock(mutex); 
    queue.push(data); 
    condition_var.notify_one(); 
} 

data pop() 
{ 
    scoped_lock lock(mutex); 
    while(queue.empty()) 
     condition_var.wait(lock); 
    return queue.pop(); 
} 

注意,這是僞十歲上下的代碼,但我相信你可以算出來。也就是說,建議使用unique_ptr(或auto_ptr for C98)來避免複製實際數據是一個好主意,但這是一個完全獨立的問題。