いくつかのコードが「待機」し、単一のスレッドがプログラムの実行を続行する場合、一時停止されたビットはどのようにして復活するのでしょうか? シングルスレッドは、一時停止されたコードビットの状態をポーリングして、終了したかどうかを確認する必要がありますか?
3 に答える
[...] 一時停止されたビットはどのようにして復活するのでしょうか?
デリゲートを使用する ( msdnから取得)。
「await」キーワードは、可能な一時停止/再開ポイントを「async」としてマークされたメソッドに挿入するようコンパイラーに指示します。
論理的には、「await someObject;」と書くと、コンパイラは、someObject によって表される操作が既に完了しているかどうかをチェックするコードを生成します。存在する場合、実行は await ポイントを超えて同期的に続行されます。そうでない場合、生成されたコードは、待機中のオブジェクトに継続デリゲートをフックし、表された操作が完了すると、その継続デリゲートが呼び出されるようにします。この継続デリゲートはメソッドを再入力し、前の呼び出しが中断されたこの await の場所から再開します。この時点で、待機中のオブジェクトが待機時までに完了しているかどうかに関係なく、オブジェクトからの結果が抽出されます。操作が失敗した場合は、発生した例外が伝播されます。
シングルスレッドは、一時停止されたコードビットの状態をポーリングして、終了したかどうかを確認する必要がありますか?
いいえ、そんなことはありません。現在のスレッドからコントロールを呼び出すとawait
、コール スタックが 1 つ上に移動します。つまり、コントロールは、 を含むメソッドの呼び出し先に返されますawait
。タスクが完了すると、TPL はデリゲート メソッドを呼び出します。デリゲート メソッドは、await
回線から制御を再開します。
が C# で具体的にどのように実装されているかは実際にはわかりませんが、「それはどのように可能ですか?」という質問に答えるために、そのawait
方法を提案させてください。(「どのように実装されているか」とは対照的に)。
最初にyield
キーワードを考えてみましょう。これはまったく異なりますが、より一般的に理解されていると思います。戻り値を返し、関数の本体内IEnumerable<T>
で使用する関数を作成すると、C# コンパイラが反復子クラスを生成し、作成したコードとは非常に異なる IL を生成yield return
することは、ほとんどの人が理解しています。要点は、C# コード自体が必ずしも一連の IL 命令に明確にマップされるとは限らないことを認識しなければならないということです。変換が複雑な場合があります。
これはawait
、C# コンパイラーが C# コードの各行に対して一連の単純なステップを単純に発行するわけではない の場合にも当てはまります。たとえば、代わりに で呼び出しを受け取り、await
指定されたメソッドを呼び出すデリゲートを生成し、残りのメソッドのコードをデリゲートへのコールバックとしてまとめることができます。
他のメソッドが非同期で動作する方法は、キーワードにとって重要ではありませんawait
。別のスレッドで作業を実行したり、一部の I/O 操作を実行したりする場合があります。Task<T>
何をするにしても、 ;を返す非同期パターンに準拠する必要があります。これがawait
頼りです。
残りのロジックをリスナーとして追加して、別のスレッドで行われている作業が完了したときに信号を受信した後、IL の結果の関数が返されます。その別のスレッドは、リスナーへの参照とともに、メッセージを元のスレッド (ほとんどのシナリオでは、実際にはメッセージ ループになります) に戻します。