複雑な計算アルゴリズムを構築しました。完了までにかなりの時間がかかります。また、アプリケーションが確実に応答するようにする必要があります。職業はなんですか?
答えはCです。しかし、A)が間違っている理由を誰かが説明できますか?なぜなら、複雑なアルゴリズムはCPUに依存しているということではありません。 CPUバウンドの場合は、タスクを使用する必要があります(タスクは、完了するまで現在のスレッドを中断させるのに役立ちますが、あまりよく理解されていない理由です)。さらに、async / awaitを使用するタイミングを決定し、タスクを使用する方法を説明してください。
職業はなんですか?
A. async / awaitを使用してください。
B.コードを同期的に実行します。
C. Task.Runを使用してください。
D. BackgroundWorkerを使用する。
のasync/await
C#5.0の機能は、同期コードを書くのと同じくらい簡単に非同期コードを作るために実装されました。 WinAPIを調べると、ほぼすべての非同期エンドポイントが完全非同期APIを公開していることがわかります。スレッドはありません。さらに調べると、同じエンドポイントが実行していることに気付くでしょう。入出力制限オペレーション。
あなたのアルゴリズムがCPUバウンドそして、複数のプロセッサに渡って効果的な計算を可能にする方法で書かれている場合(例えば、あなたのアルゴリズムは同期を必要とする共有状態を持たない)、タスク並列ライブラリ.NET 4.0で導入されました。 TPLはThreadPoolを抽象化したもので、マルチプロセッサ環境で作業を均等にオフロードしようとします。
のawait
キーワードはどのキーワードでも使用できます待つオブジェクト、つまりオブジェクトがGetAwaiter
方法。使いたいawait
awaitableを使用していて、そのwaitwaitが作業を終えたときに実行を再開したいとき。 ATask
awaitableパターンを実装し、将来完成する予定の作業単位を記述するために使用されます。あなたが使用することができますTask.Run
そしてawait
ワーカースレッドで作業単位をオフロードしたいときに、その作業が完了するまで呼び出し側のメソッドに制御を戻す。
複数のプロセッサにまたがってCPUバウンドの作業をディスパッチするもう1つの方法は、Parallel
公開するクラスParallel.For
そしてParallel.ForEach
。
ちなみに、BackgroundWorker
バックグラウンドスレッドの作業をオフロードするために使用することもできます(質問をした人がその後に行ったことがそれである場合)。それはおすすめ.NET 4.0がリリースされてからTPLを使用する方法
結論として、タスク並列ライブラリを使用することはバックグラウンドスレッドに作業をオフロードするための推奨される方法です。あなたはそれらと組み合わせてそれらを使用することができますParallel
あなたのアルゴリズムの並列性を最大にするためのライブラリ。そうした後、コードをテストするマルチスレッドを使用するオーバーヘッドがalgoを同期的に実行するのにかかる時間を超えないようにするためです。
編集する
Stephanがコメントで述べたように、あなたは両方を組み合わせることができますParallel.ForEach
そしてTask.Run
バックグラウンドスレッド内で並列ループをオフロードするには
var task = Task.Run(() => Parallel.ForEach(.. //Loop here));
await Task.Run(() => Parallel.ForEach(...))
UIが非同期的に扱うバックグラウンドスレッドに対して並列処理を行います。 - Stephen Cleary
私は、「計算」が他の情報がない場合にはCPUバウンドアルゴリズムを意味すると思います。アルゴリズムがIOに制限されている場合、async / awaitは許容できるだけでなく、正しい答えです。
Task.Run
渡されたデリゲートは、UIスレッド以外のスレッドで実行される可能性があります。あなたのUIは反応し続けるでしょう次の方法でタスクの完了をブロックしない場合はWait()
またはResult
。タスクの完了を処理し、呼び出したスレッドと通信する方法Task.Run
しかし、そもそも、あなた次第です。await
または、適切なタスク継続を使用してタスク継続を使用できます。TaskScheduler
または、元のSynchronizationContextを明示的にキャプチャして、デリゲート内からそれにポストバックすることもできます。 - Kirill ShlenskiyDispatcher.Invoke
またはControl.Invoke
または、独自のAsyncEnumeratorをロールバックすることもできます(blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx)私のポイントは、それを実行するには約100万の方法があり、そしてasync/await
たとえそれが皆の頭に浮かぶ最初のことであっても、たまたまそれらのうちの1つである(それは全く理解できる) - Kirill Shlenskiy
私は質問があなたが同期的にプログラムされたアルゴリズムを持っていると仮定すると信じます。質問は、それがCPUバウンドであることを漠然と暗示している「計算」であると述べています。それはまたアルゴリズムが同期的に書かれていることを非常に漠然と暗示しています。例えば:
public int CalculateStuff() {
....
}
私が検討することは、このメソッドに非同期のものを作成することです。
public async Task<int> CalculateStuffAsync() {
return await Task.Run(() => CalculateStuff());
}
それからあなたのユーザーインターフェースで:
LoadingIndicator.IsEnabled = true;
ResultTextBox.Text = await CalculateStuffAsync();
LoadingIndicator.IsEnabled = false;
それゆえ、正しい答えはおそらくAとCの両方でしょう。いずれにせよ、質問はあまりに曖昧すぎていかなる結論も引き出すことができません。
xxxAsync
CPUバウンドの作業のためのラッパーはデザインが良くありません。呼び出し元がラップできないようなものではありませんCalculateStuff
にTask.Run
必要ならば。次のことも考慮してください。CalculateStuffAsync().Wait()
。なしConfigureAwait(false)
あなたが書いたまでそこになかったデッドロックバグを導入したばかりですasync
ほとんどゼロの利益のための方法。 - Kirill ShlenskiySynchronizationContext
(つまりWindows Forms / WPF)そして何が起こるか見てください。これはあなたのawait
オリジナルに移行する必要がありますSynchronizationContext
非同期操作の結果が返される前に。次の方法でスレッドをブロックした場合Task.Wait
スレッドがタスクの待機中にビジーになるため(このタスクは完了しません)、この遷移は発生しません。チェックメイト取り出すasync/await
タスクを直接返すだけで解決しました。 - Kirill ShlenskiyConfigureAwait(false)
明らかに) - Kirill Shlenskiy