スレッドのプールを持つプログラムの設計に問題があります。
私が立ち往生している主な問題は、親がスレッドIDを待機する必要がある作業でスレッドが完了したときです(これは、親がpthread_joinを使用してスレッドを待機する方法です)。どのスレッドが最初に終了するかわからないため、プログラムで問題を解決する方法を見つけることができません。
コードの小さなスニペットを使用した説明をいただければ幸いです。
ありがとう。
スレッドのプールを持つプログラムの設計に問題があります。
私が立ち往生している主な問題は、親がスレッドIDを待機する必要がある作業でスレッドが完了したときです(これは、親がpthread_joinを使用してスレッドを待機する方法です)。どのスレッドが最初に終了するかわからないため、プログラムで問題を解決する方法を見つけることができません。
コードの小さなスニペットを使用した説明をいただければ幸いです。
ありがとう。
設定や要件はわかりませんが、pthread_join()が正確に1つのスレッドを待機し、実際には任意のスレッドを待機したいという問題が発生しました。
したがって、最も明白な結論は、pthread_joinは役に立たないということです。明白なことを述べて申し訳ありませんが、私は私のケースを構築する必要があります:-)
代わりに、おそらく別のアイデアを考え出す必要があります。たとえば、条件変数を待つことができます。スレッドが終了する前に、条件を「終了」に設定します。メインスレッドはその状態を待機し、スレッドを反復処理して、どのスレッドが終了したか(複数の場合もあります)を見つけ、最終的に状態をリセットすることができます。条件ミューテックスは通常、レースを防ぐのに十分です。
スレッドは、条件の設定に加えて、終了したスレッドのリストにIDを追加することもできます(条件ミューテックスを使用してそのリストを保護できます)。したがって、メインスレッドは、すべてのスレッドをチェックするのではなく、そのリストを通過するだけで済みます。
擬似コード:
initialize condition variable with status "not exited"
...
...
launch your threads
...
...
while (some threads are still running) do
lock condition variable on "exited"
iterate through threads, remove the ones that have exited
unlock condition variable with new condition "not exited"
スレッドの内部:
...
do whatever it needs to do
...
...
lock condition variable
unlock condition variable with new condition "exited"
/* end of thread */
プールは通常、プロデューサー/コンシューマー キューでタスク アイテムを待機するスレッドの数として実装されます。タスクは、「run()」メソッドを持つ Task クラスから派生したオブジェクトです。どちらのスレッドがタスクを取得しても run() を呼び出し、それが戻ると、スレッドはループしてキューから別のタスク オブジェクトを取得します。
これにより、スレッドのマイクロ管理が吹き飛ばされ、これまでに試したすべてのシステム/言語で確実かつ安全に動作します。
完了通知について私が知っている最も柔軟な方法は、run() が戻ったときに、次のタスクを取得するためにループバックする直前に、スレッドがタスクの「OnComplete」イベント、またはおそらく仮想「完了」メソッドを呼び出すことです。パラメータとしてのタスク。このメソッド/イベントは、たとえば、タスクを生成したスレッドが待機しているイベント/condvar/sema を通知したり、完了したタスクを元のスレッドまたは別のスレッドにキューに入れたり、単にタスクを削除 () したりすることもできます (おそらくジョブはスレッド プールで完全に完了します)。
エラー通知のために、run() によってスローされたキャッチされていない例外をキャッチし、その例外を tast フィールドに格納してから、完了メソッド/イベントを呼び出します。
プロデューサー/コンシューマー キューを保護するロック以外には、ロックはありません (そして、*タスクをプッシュ/ポップするのに十分な時間だけ使用されます)。
どちらのデザインを使用する場合でも、次のことを避けるように努めてください。
1) continually create/terminate/destroy threads - avoidable overhead and tricky to manage
2) wait with Join() for any thread to terminate - just don't :)
3) loop around some 'poll thread status' to see if they're finished yet - gets it wrong
4) Move 'working/finished' threads into and out of containers with complicated locks - deadlock-in-the-making
5) use any other sort of micro-management - difficult, messy, error-prone, too many locks, unnecesary, avoidable
理想的なスレッド プールは、どのスレッドが作業を行ったかがわからない場所です。実際、通常、スレッドへの参照を保持する必要さえありません。2 行の疑似スレッドプール:
TblockingQueue *inQueue=new TblockingQueue();
for(int i=0;i<CpoolDepth,i++) new Thread(inQueue);
スレッドプールを実装しようとしている場合は、数年前に私が遊んだこのアーキテクチャを見てください。私はここにそれを書きました:C++でのスレッドプール
非ブロッキングpthread_joinについては、このSOの説明を参照してください:非ブロッキングpthread_join
すべてのスレッドが作業を終了するのを待っているだけの場合は、スレッドを1つずつ待つことができます。順序は関係なく、条件を使用する理由はありません。この例は、次のことを示しています。
#include <memory.h>
#include <pthread.h>
#include <iostream>
using namespace std;
#define NTHREADS 10
void *thread(void *arg) {
int *n = (int *) arg;
sleep(10 - *n);
cout << "Thread " << (*n) << endl;
delete n;
return NULL;
}
int main(int argc, char **argv) {
pthread_t threads[NTHREADS];
pthread_attr_t attr;
memset(&attr, 0, sizeof(attr));
int i;
for (i=0; i<NTHREADS; i++) {
int *p = new int;
*p = i;
pthread_create(threads + i, &attr, thread, p);
}
void *rval;
for (i=0; i<NTHREADS; i++) {
pthread_join(threads[i], &rval);
cout << "Joined thread " << i << endl;
}
return 0;
}
スレッドの順序は待機とは逆の順序で完了しますが(つまり、スレッド0は最後に終了しますが、最初にスレッド0で待機します)、メインスレッドはすべてのスレッドが終了するまで終了しません。条件は必要ありません。