非同期/待機によってプログラムが停止しないようにする方法に本当に興味があります。StephenClearyがasync / awaitを説明する方法が本当に好きです:「「await」を「非同期待機」と考えるのが好きです。つまり、asyncメソッドはawaitableが完了するまで(つまり待機するまで)一時停止しますが、実際のスレッドはブロックされません(したがって、非同期です)。」
コンパイラがawaitキーワードに出会うまで、asyncメソッドが同期的に機能することを読みました。上手。コンパイラが待機可能かどうかを判断できない場合、コンパイラは待機可能キューをキューに入れ、メソッドを呼び出したメソッドに制御を譲りますAccessTheWebAsync
。OK。呼び出し元(この例ではイベントハンドラー)内で、処理パターンが続行されます。発信者は、その結果をAccessTheWebAsync
待つ前に、結果に依存しない他の作業を行う場合や、発信者がすぐに待つ場合があります。イベントハンドラーはを待機してAccessTheWebAsync
おり、AccessTheWebAsync
を待機していGetStringAsync
ます。msdnの例を見てみましょう:
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
msdnブログの別の記事によると、async / awaitは新しいスレッドを作成したり、スレッドプールから他のスレッドを使用したりしません。OK。
私の質問:
async / awaitが待機可能なコードを実行する場所(この例ではWebサイトのダウンロード)により、プログラムのコードの次の行に制御が譲られ、プログラムは結果を尋ねるだけTask<string> getStringTask
ですか?新しいスレッドやスレッドプールが使用されていないことはわかっています。
私は、CLRが現在の実行可能コードとメソッドの待機可能な部分を1つのスレッドのスコープ内で相互に切り替えるだけであるという愚かな仮定に正しいですか?ただし、加数の順序を変更しても合計は変更されず、UIが気付かないうちにブロックされる可能性があります。
async / awaitが待機可能なコードを実行する場所(この例ではWebサイトのダウンロード)により、プログラムのコードの次の行に制御が譲られ、プログラムはタスクgetStringTaskの結果を要求するだけですか?新しいスレッドやスレッドプールが使用されていないことはわかっています。
操作が本当に非同期である場合、「実行」するコードはありません。すべてがコールバックを介して処理されていると考えることができます。HTTPリクエストが(同期的に)送信HttpClient
され、を完了するコールバックが登録されますTask<string>
。ダウンロードが完了すると、コールバックが呼び出され、タスクが完了します。これより少し複雑ですが、それが一般的な考え方です。
非同期操作をスレッドレスにする方法について詳しく説明したブログ投稿があります。
私は、CLRが現在の実行可能コードとメソッドの待機可能な部分を1つのスレッドのスコープ内で相互に切り替えるだけだという愚かな仮定に正しいですか?
これは部分的に真のメンタルモデルですが、不完全です。1つには、async
メソッドが再開するときに、その(以前の)呼び出しスタックが一緒に再開されないことです。したがって、async
/await
は、同様のことを実行するために使用できますが、ファイバーやコルーチンとは大きく異なります。
代わりの思考のawait
「他のコードへの切り替え」として、「不完全なタスクを返す」と考えます。呼び出し元のメソッドもを呼び出すとawait
、不完全なタスクなども返されます。最終的には、不完全なタスクをフレームワーク(ASP.NET MVC / WebAPI / SignalR、または単体テストランナーなど)に返します。または、async void
メソッド(UIイベントハンドラーなど)があります。
操作の進行中に、タスクオブジェクトの「スタック」ができあがります。実際のスタックではなく、依存関係ツリーです。各async
メソッドはタスクインスタンスによって表され、それらはすべてその非同期操作が完了するのを待っています。
メソッドの待機可能な部分の継続はどこで実行されますか?
タスクを待機しているときawait
、デフォルトasync
では、キャプチャされたコンテキストでそのメソッドを再開します。このコンテキストは、そうでSynchronizationContext.Current
ない場合はそうでありnull
、そうでない場合はTaskScheduler.Current
です。実際には、これはasync
、UIスレッドで実行されているメソッドがそのUIスレッドで再開されることを意味します。async
ASP.NET要求を処理するメソッドは、同じASP.NET要求の処理を再開します(おそらく別のスレッドで)。他のほとんどの場合、async
メソッドはスレッドプールスレッドで再開されます。
質問のサンプルコードでGetStringAsync
は、不完全なタスクが返されます。ダウンロードが完了すると、そのタスクは完了します。そのため、そのダウンロードタスクをAccessTheWebAsync
呼び出すawait
と(ダウンロードがまだ完了していないと仮定して)、現在のコンテキストがキャプチャされ、から不完全なタスクが返されAccessTheWebAsync
ます。
ダウンロードタスクが完了すると、の継続はAccessTheWebAsync
そのコンテキスト(UIスレッド、ASP.NET要求、スレッドプールなど)にスケジュールされ、そのコンテキストでのLength
実行中に結果のを抽出します。ときにAccessTheWebAsync
メソッドが返す、それは以前から返されたタスクの結果を設定しますAccessTheWebAsync
。これにより、次のメソッドなどが再開されます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加