2016-11-03 120 views
1

我有關於C++多線程的問題。我有一個場景如下如何在不進行輪詢的情況下等待線程?

void ThreadedRead(int32_t thread_num, BinReader reader) { 
    while (!reader.endOfData) { 
     thread_buckets[thread_num].clear(); 
     thread_buckets[thread_num] = reader.readnextbatch() 
     thread_flags[thread_num] = THREAD_WAITING; 
     while (thread_flags[thread_num] != THREAD_RUNNING) { 
      // wait until awakened 
      if (thread_flags[thread_num] != THREAD_RUNNING) { 
       //go back to sleep 
      } 
     } 
    } 
    thread_flags[thread_num] = THREAD_FINISHED; 
} 

沒有上述代碼段寫入或訪問線程之間共享的內存。每個線程都分配了一個thread_num和一個可用於讀取數據的唯一讀取器對象。

我想讓主線程能夠通知處於THREAD_WAITING狀態的線程,他的狀態已經改回到THREAD_RUNNING,他需要做一些工作。我不想讓他繼續投票他的狀態。

我瞭解條件變量和互斥鎖可以幫助我。但我不知道如何使用它們,因爲我不想獲取或需要鎖定。 mainthread毯子如何通知所有等待的線程,他們現在可以自由閱讀更多數據?

編輯: 萬一有人需要更多的細節

1)閱讀器讀取一些文件 2)thread_buckets是UINT16 3向量的向量)threadflags是INT矢量

他們都已適當調整大小

+2

只需使用一個條件變量,需要您的簡歷鎖,因爲調用cv.wait以外的情況下可能發生的數據競爭( )或cv.notify()。 –

+0

但沒有數據以共享方式被訪問?每個線程都有自己的數據成員,它操縱。我希望他們以非互斥的方式運行。我只是希望他們等到主線程說他們可以繼續前進 –

回答

1

看起來像有幾個問題。一方面,您不需要循環內的條件:

while (thread_flags[thread_num] != THREAD_RUNNING); 

將自行工作。只要該條件爲假,循環將退出。

如果你想要做的就是避免儘快檢查thread_flags,只是把循環產率:

while (thread_flags[thread_num] != THREAD_RUNNING) yield(100); 

這將導致線程讓出CPU,以便它可以做其他事情而線程等待其狀態改變。這將使投票的開銷接近可忽略不計。你可以嘗試睡眠時間來找到一個很好的價值。 100ms可能是長線。

根據導致線程狀態改變的原因,你可以讓線程直接輪詢該條件/值(在睡眠狀態下)並且根本不打擾狀態。

這裏有很多選項。如果你查看讀者線程,你可能會找到你想要的;有一個單獨的閱讀器線程是非常普遍的。

+0

,但是有問題的任務可以比1000ms更快地完成我只是想等到任務完成時才需要:( –

+0

'sleep '不是一個好的產品代碼 – slawekwin

+0

同意這兩個計數我更新了我的答案,使用100ms代替(仍然很高但可能更接近你想要的)並使用yield()作爲通用的線程產生函數來使它該線程在等待時不應占用CPU資源。 – Andrew

3

我意識到你寫了你想避免條件變量和鎖。另一方面,你提到這是因爲你不確定如何使用它們。請考慮下面的例子把工作無投票完成:

與條件變量的訣竅是,一個單一的condition_variable對象與單一mutex對象一起會做管理,你包括在該unique_lock對象的處理工作者線程。由於您將問題標記爲C++,因此我假設您正在討論C++ 11(或更高版本)多線程(我猜C-pthreads可能會以類似的方式工作)。您的代碼可能如下:

// compile for C++11 or higher 

#include <thread> 
#include <condition_variable> 
#include <mutex> 

// objects visible to both master and workers: 
std::condition_variable cvr; 
std::mutex    mtx; 

void ThreadedRead(int32_t thread_num, BinReader reader) { 
    while (!reader.endOfData) { 
     thread_buckets[thread_num].clear(); 
     thread_buckets[thread_num] = reader.readnextbatch() 

     std::unique_lock<std::mutex> myLock(mtx); 
     // This lock will be managed by the condition variable! 

     thread_flags[thread_num] = THREAD_WAITING; 
     while (thread_flags[thread_num] == THREAD_WAITING) { 
      cvr.wait(myLock); 
     // ...must be in a loop as shown because of potential spurious wake-ups 
     } 
    } 
    thread_flags[thread_num] = THREAD_FINISHED; 
} 

(重新)從主線程激活工人:

{ // block... 
// step 1: usually make sure that there is no worker still preparing itself at the moment 
    std::unique_lock<std::mutex> someLock(mtx); 
    // (in your case this would not cover workers currently busy with reader.readnextbatch(), 
    // these would be not re-started this time...) 

// step 2: set all worker threads that should work now to THREAD_RUNNING 
    for (...looping over the worker's flags...) { 
    if (...corresponding worker should run now...) { 
     flag = THREAD_RUNNING; 
    } 
    } 

// step 3: signalize the workers to run now 
    cvr.notify_all(); 

} // ...block, releasing someLock 

注意:

  • 如果你只是想觸發所有睡眠你應該用一個標誌來控制它們,而不是一個標誌的容器。
  • 如果您想要觸發單個睡眠工作人員,但是哪一個人考慮.notify_one()成員函數而不是.notify_all()並不重要。還要注意,在這種情況下,單個互斥/條件變量對就足夠了。
  • 這些標誌最好放在atomic對象中,例如全局的std::atomic<int>或可能爲了更好地控制std::vector<std::atomic<int>>
  • 一個很好的介紹std::condition_variable這也激發了建議的解決方案給出的:cplusplus website
相關問題