2016-11-03 4 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ブランケットは、待機中のすべてのスレッドに、現在、より多くのデータを読み込むことが自由であることをどのように通知できますか?

EDIT:誰もが詳細

1を必要とする 念のために)読者は、いくつかのファイル 2を読み込む)thread_buckets uint16の 3)threadflagsのベクトルのベクトルがintベクトル

です彼らはすべて持っています適切にサイズが変更されました

+2

cv.waitを呼び出すときにそうでない場合は、データの競合が発生する可能性があるため、ロックが(CVのために必要とされます)またはcv.notify()。 –

+0

共有された方法でデータがアクセスされていませんか?すべてのスレッドには、それが操作する独自のデータメンバーがあります。私はそれらを相互に排他的ではない方法で走らせたい。私はただ彼らがメインスレッドが彼らが先に進むことが大丈夫だと言うまで待ちたいと思う。 –

答えて

1

いくつか問題があるようです。 1つは、ループの条件付き内部は必要ありません。

while (thread_flags[thread_num] != THREAD_RUNNING); 

は単独で動作します。その条件が偽になると、ループは終了します。

あなたがしたいすべてがちょうどループで歩留まりを入れて、可能な限り迅速にthread_flagsをチェックしないようであれば:

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

これは、他のことを行うことができるようにスレッドがCPUをもたらすことになりますスレッドはその状態が変化するのを待つ。これにより、ポーリングのオーバーヘッドをごくわずかにすることができます。あなたは良い値を見つけるために睡眠時間を試すことができます。おそらく100ミリメートルは長いです。

スレッド状態を変更する原因に応じて、スレッドの状態/値を直接(スリープ状態で)ポーリングし、状態をまったく気にしないようにすることができます。

ここには多くのオプションがあります。読者のスレッドを調べるなら、おそらくあなたが望むものだけを見つけることができます。別のリーダスレッドを持つことは非常に一般的です。

+0

しかし、問題のタスクは1000ミリ秒よりも速く完了する。私はちょうどタスク完了まで必要なだけ待つことを望む:( –

+0

'sleep 'は良いプロダクションコードではありません – slawekwin

+0

両方の点で合意しました。代わりに100msを使用するように回答を更新しました(まだ高いですが、あなたが望むものに近いでしょう)。yield()待機中にスレッドがCPUリソースを占めるべきではないことを明白に示します。 – Andrew

3

私は、あなたが条件変数とロックを避けたいと書いたことを認識しています。一方、あなたは、これを使用する方法がわからないためにこれが言及されました。ジョブは、ポーリングなしに成し遂げるために、次の例を考えてみてください:

条件変数とトリックが単一mutexのオブジェクトと一緒に単一condition_variableオブジェクトはあなたが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 

お知らせ:

  • あなただけのすべての睡眠をトリガしたい場合フラグのコンテナの代わりに単一のフラグでそれらを制御する必要があります。
  • 1人の睡眠中の労働者を誘発したいが、.notify_all()の代わりに.notify_one()メンバ関数を考慮する必要はありません。この場合も、単一のmutex/condition_variableペアで十分であることに注意してください。
  • フラグは、グローバルstd::atomic<int>のようなatomicオブジェクトに配置するのが良いか、またはstd::vector<std::atomic<int>>の細かい制御のために配置するのが良いでしょう。
  • も提案された解決策を触発std::condition_variableの入門がで与えられます。ただ、条件変数を使用cplusplus website
関連する問題