必要な概念はスレッドプールです。このSOの質問は、既存の実装を扱います。
アイデアは、多数のスレッドインスタンス用のコンテナを持つことです。各インスタンスは、タスクキューをポーリングする関数に関連付けられており、タスクが使用可能になると、タスクをプルして実行します。タスクが終了すると(終了した場合でも、それは別の問題です)、スレッドは単にタスクキューにループオーバーします。
したがって、同期キュー、キューのループを実装するスレッドクラス、タスクオブジェクトのインターフェイス、およびすべてを駆動するクラス(プールクラス)が必要です。
または、実行する必要のあるタスク用に非常に特殊なスレッドクラスを作成することもできます(たとえば、パラメーターとしてメモリ領域のみを使用します)。これには、スレッドが現在の反復で完了したことを示す通知メカニズムが必要です。
スレッドのメイン関数はその特定のタスクのループであり、1回の反復の終わりに、スレッドはその終了を通知し、条件変数を待って次のループを開始します。本質的には、スレッド内でタスクコードをインライン化し、キューの必要性を完全になくします。
using namespace std;
// semaphore class based on C++11 features
class semaphore {
private:
mutex mMutex;
condition_variable v;
int mV;
public:
semaphore(int v): mV(v){}
void signal(int count=1){
unique_lock lock(mMutex);
mV+=count;
if (mV > 0) mCond.notify_all();
}
void wait(int count = 1){
unique_lock lock(mMutex);
mV-= count;
while (mV < 0)
mCond.wait(lock);
}
};
template <typename Task>
class TaskThread {
thread mThread;
Task *mTask;
semaphore *mSemStarting, *mSemFinished;
volatile bool mRunning;
public:
TaskThread(Task *task, semaphore *start, semaphore *finish):
mTask(task), mRunning(true),
mSemStart(start), mSemFinished(finish),
mThread(&TaskThread<Task>::psrun){}
~TaskThread(){ mThread.join(); }
void run(){
do {
(*mTask)();
mSemFinished->signal();
mSemStart->wait();
} while (mRunning);
}
void finish() { // end the thread after the current loop
mRunning = false;
}
private:
static void psrun(TaskThread<Task> *self){ self->run();}
};
classcMyTask {
public:
MyTask(){}
void operator()(){
// some code here
}
};
int main(){
MyTask task1;
MyTask task2;
semaphore start(2), finished(0);
TaskThread<MyTask> t1(&task1, &start, &finished);
TaskThread<MyTask> t2(&task2, &start, &finished);
for (int i = 0; i < 10; i++){
finished.wait(2);
start.signal(2);
}
t1.finish();
t2.finish();
}
上記で提案された(粗い)実装は、Task
提供しなければならないタイプoperator()
(つまり、クラスのようなファンクター)に依存しています。先ほどスレッド関数本体に直接タスクコードを組み込むことができると言ったのですが、わからないのでできるだけ抽象化していきました。スレッドの開始用と終了用の条件変数が1つあり、どちらもセマフォインスタンスにカプセル化されています。
の使用を提案している他の答えを見て boost::barrier
、私はこのアイデアをサポートすることしかできません:可能であれば、セマフォクラスをそのクラスに置き換えるようにしてください。その理由は、自己実装ではなく、十分にテストされ維持されている外部コードに依存する方が良いからです。同じ機能セットのソリューション。
全体として、どちらのアプローチも有効ですが、前者は柔軟性を優先してわずかなパフォーマンスを放棄します。実行するタスクに十分な時間がかかる場合、管理とキューの同期のコストはごくわずかになります。
更新:コードが修正され、テストされました。単純な条件変数をセマフォに置き換えました。