コルーチンとは c ++ 20?
「Parallelism2」または「および/または」「Concurrency2」(下記の画像をご覧ください)とはどのように異なりますか?
以下の画像はISOCPPからのものです。
コルーチンとは c ++ 20?
「Parallelism2」または「および/または」「Concurrency2」(下記の画像をご覧ください)とはどのように異なりますか?
以下の画像はISOCPPからのものです。
回答:
抽象レベルでは、コルーチンは実行の状態を持つという考えを、実行のスレッドを持つという考えから切り離しました。
SIMD(単一命令の複数データ)には複数の「実行スレッド」がありますが、実行状態は1つだけです(複数のデータに対してのみ機能します)。間違いなく並列アルゴリズムは、このようなビットであり、1つの「プログラム」をさまざまなデータで実行します。
スレッディングには、複数の「実行スレッド」と複数の実行状態があります。複数のプログラムと複数の実行スレッドがあります。
コルーチンには複数の実行状態がありますが、実行のスレッドを所有していません。あなたにはプログラムがあり、プログラムには状態がありますが、実行のスレッドはありません。
コルーチンの最も簡単な例は、他の言語からのジェネレーターまたは列挙型です。
擬似コードでは:
function Generator() {
for (i = 0 to 100)
produce i
}
Generator
呼ばれ、最初の時間は、それはそれを返すと呼ばれています0
。その状態は記憶され(コルーチンの実装によってどの程度の状態が変化するか)、次に呼び出すときに、中断したところから続行されます。そのため、次回は1を返します。次に2。
最後に、ループの終わりに達し、関数の終わりから落ちます。コルーチンが完成しました。(ここで発生することは、話している言語によって異なります。Pythonでは、例外がスローされます)。
コルーチンはこの機能をC ++にもたらします。
コルーチンには2種類あります。スタックフルとスタックレス。
スタックレスコルーチンは、ローカル変数をその状態と実行場所にのみ格納します。
スタックコルーチンは、スタック全体(スレッドのような)を格納します。
スタックレスコルーチンは非常に軽量にできます。私が読んだ最後の提案には、基本的に関数をラムダのようなものに書き直すことが含まれていました。すべてのローカル変数はオブジェクトの状態になり、ラベルはコルーチンが中間結果を「生成する」場所に、またはそこからジャンプするために使用されます。
コルーチンは協調マルチスレッドのように少しなので、値を生成するプロセスは「yield」と呼ばれます。実行ポイントを呼び出し元に戻します。
Boostには、スタックフルコルーチンの実装があります。それはあなたがあなたのために譲るための関数を呼び出すことを可能にします。スタックコルーチンはより強力ですが、より高価でもあります。
コルーチンには、単純なジェネレーターだけではありません。コルーチンでコルーチンを待つことができるため、コルーチンを便利な方法で作成できます。
if、ループ、関数呼び出しなどのコルーチンは、特定の有用なパターン(状態マシンなど)をより自然な方法で表現できる、別の種類の「構造化goto」です。
C ++でのコルーチンの特定の実装は、少し興味深いものです。
最も基本的なレベルでは、いくつかのキーワードをC ++に追加co_return
co_await
co_yield
します。
関数は、それらの1つを本体に入れることによってコルーチンになります。したがって、それらの宣言から、関数と区別がつきません。
これら3つのキーワードの1つが関数本体で使用されると、戻り値の型と引数の標準的な必須の検査が行われ、関数がコルーチンに変換されます。この検査は、関数が中断されたときに関数の状態を格納する場所をコンパイラーに指示します。
最も単純なコルーチンはジェネレータです。
generator<int> get_integers( int start=0, int step=1 ) {
for (int current=start; true; current+= step)
co_yield current;
}
co_yield
関数の実行を一時停止し、その状態をに保存してから、generator<int>
をcurrent
介しての値を返しますgenerator<int>
。
返された整数をループできます。
co_await
その間、1つのコルーチンを別のコルーチンにスプライスできます。あなたが1つのコルーチンにいて、進行する前に待てばよいもの(多くの場合コルーチン)の結果が必要な場合はco_await
、それを実行します。準備ができている場合は、すぐに続行します。そうでない場合は、待機しているawaitableの準備ができるまで中断します。
std::future<std::expected<std::string>> load_data( std::string resource )
{
auto handle = co_await open_resouce(resource);
while( auto line = co_await read_line(handle)) {
if (std::optional<std::string> r = parse_data_from_line( line ))
co_return *r;
}
co_return std::unexpected( resource_lacks_data(resource) );
}
load_data
std::future
名前付きリソースが開かれたときにを生成するコルーチンであり、要求されたデータが見つかったポイントまで解析できます。
open_resource
とread_line
sはおそらくファイルを開き、そこから行を読み取る非同期コルーチンです。co_await
停止と準備状態を接続load_data
の進捗状況に。
C ++コルーチンは、ユーザー空間型の上に最小限の言語機能セットとして実装されたため、これよりもはるかに柔軟です。ユーザースペースタイプは効果的に何co_return
co_await
をco_yield
意味するかを定義します- co_await
空のオプションのa が自動的に空の状態を外側のオプションに伝達するようにモナディックオプション式を実装するためにそれを使用するのを見てきました:
modified_optional<int> add( modified_optional<int> a, modified_optional<int> b ) {
return (co_await a) + (co_await b);
}
の代わりに
std::optional<int> add( std::optional<int> a, std::optional<int> b ) {
if (!a) return std::nullopt;
if (!b) return std::nullopt;
return *a + *b;
}
;;
。
コルーチンは、他のルーチンが完了するのを「待機」し、中断、一時停止、待機、ルーチンの続行に必要なものを提供できる(C ++の)関数であると想定されています。C ++の人々にとって最も興味深い機能は、コルーチンがスタックスペースを取らないのが理想的です... C#は、awaitとyieldを使用してすでにこのようなことを実行できますが、C ++を組み込むために再構築する必要がある場合があります。
並行性は、懸念がプログラムが完了することになっているタスクである場合の懸念の分離に重点を置いています。この懸念の分離は、いくつかの方法で実現できます。通常、何らかの委任になります。並行性の考え方は、いくつかのプロセスが独立して実行できる(懸念の分離)ことであり、「リスナー」は、それらの分離された懸念によって生成されるものを、それがどこに行くことになっているところにでも向けることです。これは、なんらかの非同期管理に大きく依存しています。並行処理には、アスペクト指向プログラミングなどを含む多くのアプローチがあります。C#には、非常にうまく機能する「デリゲート」演算子があります。
並列処理は並行性のように聞こえ、関与する可能性がありますが、実際には、コードの一部を実行する場所の異なるプロセッサにコードを送り、結果を受け取ることができるソフトウェアと並列に配置された多くのプロセッサを含む物理的な構造です同期的に。