私は新しい非同期について読んでいますawait
これは素晴らしいキーワードですが、これまで見てきたイントロビデオで答えを見つけることができなかった重要な質問が1つあります(しばらく前にホワイトペーパーを読んでいます)。
に電話があるとします。await
メインUIスレッドの入れ子関数この時点でスレッドはどうなりますか?制御はメッセージループに戻り、UIスレッドは他の入力を自由に処理できますか?
待機中のタスクが完了すると、スタック全体がメッセージキューにプッシュされ、制御がそれらのネストされた各関数を介して返されるようになりますか。それとも、ここで何か他のことが起こっていますか。
第二に(私があなたの注意を払っている間)、なぜ非同期メソッドがでラベル付けされる必要があるのか、私は本当にわかりませんasync
。どのメソッドも非同期に実行できませんか?メソッドを非同期的に実行したいのにasyncキーワードがない場合はどうなりますか?それを簡単に行う方法はありますか?
乾杯。 :)
編集する確かに、サンプルコードをコンパイルすることができれば、おそらく自分でそれを理解することしかできませんでしたが、何らかの理由でそこにブロックがあります。私が本当に知りたいのは、継続がどの程度継続するかです。それは、タスクが完了したときに再開するために、コールスタック全体をフリーズしますか、それともそれだけで戻りますか?継続をサポートするために関数自体を非同期としてマークする必要がありますか?それとも(私が最初に尋ねたように)それは呼び出しスタック全体を継続しますか?
それであればしない呼び出しスタック全体をフリーズします。非同期待ちが非同期呼び出し関数にぶつかるとどうなりますか?それはそこでブロックしますか?それは待ち望んでいた点を破ったのではないでしょうか。私はこれを学び続けることができるように誰かが記入することができることを私がここに理解を欠いていることをあなたが見ることができることを願っています。
メインのUIスレッドで入れ子になった関数で待つための呼び出しがあるとします。この時点でスレッドはどうなりますか?制御はメッセージループに戻り、UIスレッドは他の入力を自由に処理できますか?
はい。あなたがawait
(aなど)Task<TResult>
)内のスレッドの現在位置async
メソッドがキャプチャされます。次に、それは、待機可能物が終了したときに実行されるように、方法の残りの部分(「継続」)を待ち行列に入れる。Task<TResult>
完了します)。
ただし、最適化が行われることがあります。awaitableが既に終了している場合は、await
待つ必要はありません、そしてそれはただただメソッドを実行し続けます。これは「高速パス」と呼ばれます。ここに記述。
待機中のタスクが完了すると、スタック全体がメッセージキューにプッシュされて、制御がそれらのネストされた各関数を介して返されるようになりますか。
スレッドの現在位置はUIメッセージキューにプッシュされます。詳細はもう少し複雑です。継続が予定されていますTaskScheduler.FromCurrentSynchronizationContext
ない限りSynchronizationContext.Current
ですnull
その場合それらは予定されていますTaskScheduler.Current
。また、この振る舞いは以下を呼び出すことによって上書きすることができます。ConfigureAwait(false)
これは常にスレッドプールでの継続をスケジュールします。からSynchronizationContext.Current
UIですSynchronizationContext
WPF / WinForms / Silverlightの場合、この継続はUIメッセージキューにプッシュされます。
第二に(私があなたの注意を払っている間に)、なぜ非同期メソッドがasyncでラベル付けされる必要があるのか、私は本当に理解していません。どのメソッドも非同期に実行できませんか?メソッドを非同期的に実行したいのにasyncキーワードがない場合はどうなりますか?それを簡単に行う方法はありますか?
これらは「非同期」という言葉の意味が少し異なります。のasync
キーワードでawait
キーワード。言い換えると、async
方法はawait
。昔ながらの非同期デリゲート(つまり、BeginInvoke
/EndInvoke
)とはかなり異なりますasync
。非同期デリゲートは、ThreadPool
スレッド、しかしasync
メソッドはUIスレッド上で実行されます(それらがUIコンテキストから呼び出され、あなたが呼び出さないと仮定します)。ConfigureAwait(false)
)
あなたが(非 - async
)メソッドはThreadPool
スレッド、あなたはこのようにそれを行うことができます:
await Task.Run(() => MyMethod(..));
私が本当に知りたいのは、継続がどの程度継続するかです。それは、タスクが完了したときに再開するために、コールスタック全体をフリーズしますか、それともそれだけで戻りますか?継続をサポートするために関数自体を非同期としてマークする必要がありますか?それとも(私が最初に尋ねたように)それは呼び出しスタック全体を継続しますか?
現在位置が取得され、継続が実行されると「再開」します。使用する機能await
継続を支援するためには印をつけなければならないasync
。
あなたが電話している場合async
非からのメソッドasync
方法、それから対処しなければなりませんTask
直接オブジェクト。これは通常行われていません。トップレベルasync
メソッドが返す可能性がありますvoid
だから、持っていない理由はありませんasync
イベントハンドラ
ご了承くださいasync
純粋にコンパイラ変換です。それはそれを意味しますasync
メソッドは、コンパイル後の通常のメソッドとまったく同じです。 .NETランタイムはそれらを特別な方法で処理しません。
Task
だから、あなたはする必要がありますawait
例外を見るためです。しかし、それからそれはちょうどコールスタックを上に移動します。 - Stephen ClearyTask
後で観察されるオブジェクト。フルスタックがキャプチャされたとしても(この場合はそうではありません)、catch
例外がに取り込まれたため、ブロックは実行されませんTask
。 - Stephen Cleary
それはAwaitableの振る舞いによります。
それは同期的に実行する、すなわちスレッド上で実行し、同じスレッド上の待機者に制御を戻すという選択肢を有する。
非同期に実行することを選択した場合、待機者は、待機者がコールバックをスケジュールしているスレッドでコールバックされます。その間、呼び出し側スレッドは解放されます。待機中の開始があるので、非同期作業で終了し、待機者はその待機に待機者のコールバックをアタッチしています。
2番目の質問に関しては、asyncキーワードは、メソッドが非同期に呼び出されるかどうかではなく、そのメソッドの本体が非同期コードをそれ自体インラインで呼び出したいかどうかということです。
すなわちTaskまたはTaskを返す任意のメソッドは非同期的に(待機またはcontinuewithで)呼び出すことができますが、asyncでマークすることで、そのメソッドは本文でawaitキーワードを使用できるようになります。 Tは、ボディ全体がタスクが実行するステートマシンに書き換えられるためです。
方法があるとしましょう
public async Task DoSomething() {
}
あなたがUIスレッドからそれを呼び出すとき、あなたはタスクを取り戻します。この時点で、タスクをブロックすることができます。.Wait()
または.Result
そのTaskSchedulerでasyncメソッドを実行するか、でブロックします。.RunSynchronously()
これはUIスレッド上で実行されます。もちろん内部で起こるどんな待ちもDoSomething
基本的には別の継続です。そのため、TaskSchedulerスレッドでコードの一部を実行してしまう可能性があります。しかし、最終的にはUIスレッドは完了するまでブロックされます。これは通常の同期メソッドとまったく同じです。
または、で継続をスケジュールすることができます.ContinueWith()
タスクが完了したときにTaskSchedulerによって呼び出されるアクションを作成します。これはすぐに現在のコードに制御を戻します。現在のコードは、UIスレッドで実行していたことをすべて実行し続けます。継続はコールスタックをとらえるのではなく、単なるActionなので、外部スコープからアクセスするすべての変数を単にとらえるだけです。