タスク

タスクは、C++11 標準に追加された最新の機能の 1 つです。スレッドよりも優れた抽象化を提供します。一般的なケースでは、それらを最初に選択する必要があります。

データ チャネルとしてのタスク

タスクはデータ チャネルのように動作します。一方では、送信者が値を設定します。反対側では、レシーバーが値を受け取ります。送信者は promise と呼ばれます 、レシーバー - 未来 .別の言い方をすれば、送信者は、受信者が将来受け取ることができる価値を提供することを約束します。

さらにいくつかの詳細。送信者は、複数の先物に値を提供できます。値の他に、送信者は通知または例外を提供することもできます。 取得 未来の呼び声ブロック .これは、将来の呼び出しが待機する場合、待機する必要があることを意味します promise が値をチャネルに入れるまで。

タスクには 3 つのバリエーションがあります。 std::async による非同期関数呼び出しとして、std::packaged_task による callable の単純なラッパーとして、および std::promise と std::future の明示的なペアとして。

スレッドとタスクの違いを理解する最善の方法は、それらを比較することです。

スレッドとタスク

この小さなコード例は違いを示しています:

int res;
std::thread t([&]{res= 3+4;});
t.join();
std::cout << res << std:::endl;

auto fut=std::async([]{return 3+4;});
std::cout << fut.get() << std::endl;

子スレッドと promise の両方が 3+4 の合計を計算し、結果を返します。 std::async 呼び出しは、エンドポイント fut と std::async の両方を持つデータ チャネルを生成します。 fut は未来、std::async は約束です。 future は fut.get() の呼び出しで値を取得します。この値は promise によって提供されます。未来は後の時点で行動することができます。

違いは何ですか?

スレッドには ヘッダーが必要で、タスクには ヘッダーが必要です。スレッドの参加者は作成者スレッドと子スレッドであり、タスクの参加者は約束と未来です。共有変数 res は、子供が計算結果を作成者に転送する方法です。反対に、promise と future は共通のデータ チャネルを使用し、std::async はデータ チャネルを作成します。fut.get を使用すると、future が結果を取得します。スレッドを使用すると、共有変数をロックで保護する必要があります。しかし、promise と future の競合状態が発生する可能性は暗黙のうちにありません。スレッドの作成者は、その子が完了するまで t.join 呼び出しを待機します。反対側では、fut.get 呼び出しがブロックされます。子スレッドで例外が発生した場合、子スレッドと作成者スレッドが終了します。したがって、最終的にプログラム全体が終了します。プロミスは未来に例外をもたらすことができます。未来は例外を処理する必要があります。子スレッドは作成者スレッドにのみ値を提供できますが、promise は値、例外、および通知を関連する Future に送信できます。

スレッドとタスクの主な違いは、タスクの抽象化レベルが高いことです。タスクはスレッドを自動的に生成しません。正確には、C++ ランタイムがスレッドを作成するかどうかを決定します。決定の理由は次のとおりです。ペイロードの重量はどれくらいですか。使用可能なコア数は?システム負荷はどれくらいですか?

次は?

これが、タスクに関する次の投稿の基礎となりました。次は std::async についてです。(校正者 Alexey Elymanov )