6

まず、背景情報を少し。WinRT での実行に適した既存の C# ライブラリ コードを作成中です。このコードの奥深くで小さなファイル IO を実行する必要がある部分があるため、最初にすべての IO が完了するまでタスクの同期を維持し、Task.Wait() を使用してメイン スレッドを停止しようとしました。

案の定、それがデッドロックにつながることがすぐにわかりました。

その後、プロトタイプを「非同期」にするために多くのコードを変更していることに気付きました。つまり、async キーワードと await キーワードを挿入し、それに応じてメソッドの戻り値の型を変更していました。これは大変な作業でした (実際には無意味な作業が多すぎます) が、この方法でプロトタイプを動作させることができました。

次に、実験を行い、別のスレッドでWait ステートメントを使用して元のコードを実行しました。

System.Threading.Tasks.Task.Run(()=> Draw(..., cancellationToken)

デッドロックなし!

非同期プログラミングがどのように機能するかを理解していると思っていたので、今、私は真剣に混乱しています。私たちのコードは、(まだ) ConfigureAwait(false) をまったく使用していません。したがって、すべての await ステートメントは、呼び出されたときと同じコンテキストで継続する必要があります。私はそれが意味すると仮定しました:同じスレッド。このスレッドが「Wait」を呼び出した場合、これもデッドロックにつながるはずです。しかし、そうではありません。

明確で堅実な説明を持っている人はいますか?

これに対する答えは、多くの条件付き async/await キーワードを挿入してコードをめちゃくちゃにするか、それともコードをきれいに保ち、あちこちで Wait() を実行するスレッドを使用するかを決定します。継続がブロックされていない任意のスレッドによって実行される場合、問題はありません。ただし、それらが UI スレッドによって実行される場合、継続の計算コストが高いと問題が発生する可能性があります。

問題が明確になることを願っています。そうでない場合は、お知らせください。

4

1 に答える 1

7

ブログに / イントロがあります。ここでは、コンテキストが何であるかを正確に説明していasyncますawait

そうでSynchronizationContext.Currentないnull場合はそうですTaskScheduler.Current。注: current がない場合TaskSchedulerは、スレッド プール タスク スケジューラである とTaskScheduler.Current同じです。TaskScheduler.Default

SynchronizationContext今日のコードでは、通常、 ;があるかどうかにかかっています。現在、タスク スケジューラはあまり使用されていません (ただし、将来的にはより一般的になるでしょう)。それがどのように機能するか、および .NET によって提供されるいくつかの実装について説明した記事があります。SynchronizationContext

WinRT およびその他の UI フレームワーク (WinForms、WPF、Silverlight) はすべてSynchronizationContext、メインの UI スレッドに を提供します。このコンテキストは 1 つのスレッドのみを表すため、ブロッキング コードと非同期コードを混在させると、すぐにデッドロックが発生する可能性があります。これが発生する理由についてはブログ投稿で詳しく説明していますが、要約すると、デッドロックが発生する理由は、asyncメソッドが再入力しようとしているSynchronizationContext(この場合は UI スレッドでの実行を再開しようとしている) ためですが、UI スレッドがそのasyncメソッドが完了するのを待ってブロックされました。

SynchronizationContextスレッド プールには(またはTaskScheduler、通常)はありません。そのため、スレッド プール スレッドで実行し、非同期コードでブロックしても、デッドロックは発生しません。これは、キャプチャされたコンテキストがスレッド プール コンテキスト (特定のスレッドに関連付けられていない) であるためです。そのため、async別のスレッド プール スレッドが待機中にブロックされている間、メソッドは (スレッド プール スレッドで実行するだけで) そのコンテキストに再入力できます。完了します。

これに対する答えは、多くの条件付き async/await キーワードを挿入して実際にコードを台無しにするかどうか、またはコードをきれいに保ち、あちこちで Wait() を実行するスレッドを使用するかどうかを決定します。

あなたのコードがasync完全であれば、乱雑に見えるべきではありません。「条件付き」の意味がわかりません。私はそれをすべて作るでしょうasyncawait操作がすでに完了している場合に同期させる「高速パス」実装があります。

バックグラウンド スレッドを使用して非同期コードをブロックすることは可能ですが、いくつかの注意事項があります。

  1. UI コンテキストがないため、多くの UI 操作を実行できません。
  2. UI スレッドに「同期」する必要があり、UI スレッドがブロックされないようにする必要があります (たとえば、ブロックすべきawait Task.Run(..)ではありませんTask.Run(..).Wait())。これは特に WinRT アプリに当てはまります。
于 2013-08-15T12:30:13.717 に答える