誰かが説明できますかawait
そしてContinueWith
次の例では、同義語であるか、そうではありません。私は初めてTPLを使おうとしていて、すべてのドキュメントを読んでいますが、違いを理解していません。
待つ:
String webText = await getWebPage(uri);
await parseData(webText);
を続行:
Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) => parseData(task.Result));
webText.Start();
continue.Wait();
特定の状況では、一方が他方よりも優先されますか?
2番目のコードでは、同期して継続が完了するのを待っています。最初のバージョンでは、最初のメソッドにヒットするとすぐにメソッドは呼び出し元に戻ります。await
まだ完成していない表現
どちらも継続をスケジュールするという点で非常によく似ていますが、制御フローが少し複雑になるとすぐに、await
につながるずっとより単純なコードさらに、Servyのコメントで述べたように、タスクを待つと、集約された例外が「ラップ解除」され、通常はエラー処理が簡単になります。また使うawait
呼び出しコンテキストで継続を暗黙的にスケジュールします(使用しない限り)。ConfigureAwait
)それは "手動で"行うことができないことではありませんが、それを使ってそれを行うことははるかに簡単ですawait
。
両方を使って、少し大きな操作シーケンスを実装してみることをお勧めします。await
そしてTask.ContinueWith
- それは本当のアイオープナーになることができます。
await
オーバーContinueWith
その点で。 - ServyparseData
で実行されます。 - Stephen Cleary
これが、非同期ソルブを使用したときの違いとさまざまな問題を説明するために最近使用した一連のコードスニペットです。
GUIベースのアプリケーションに時間がかかるイベントハンドラがあり、それを非同期にしたいとします。これが同期ロジックです。
while (true) {
string result = LoadNextItem().Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
LoadNextItemはTaskを返します。これにより、最終的に検査したい結果が得られます。現在の結果があなたが探しているものであれば、あなたはUI上のいくつかのカウンタの値を更新し、そしてメソッドから戻る。それ以外の場合は、LoadNextItemからさらにアイテムを処理し続けます。
非同期バージョンのための最初のアイデア:継続を使うだけ!そして、当分の間ループ部分を無視しましょう。つまり、どうすればうまくいかないのでしょうか。
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
});
グレート、今私たちはブロックしない方法を持っています!代わりにクラッシュします。 UIコントロールへの更新はすべてUIスレッドで行われる必要があるので、それを考慮する必要があります。ありがたいことに、継続をスケジュールする方法を指定するオプションがあります。これにはデフォルトのものがあります。
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
素晴らしい、今私達はクラッシュしない方法を持っています!代わりにそれは静かに失敗します。継続はそれ自体が別々のタスクであり、そのステータスは先行タスクのステータスとは関係ありません。したがって、LoadNextItemに障害が発生した場合でも、呼び出し元には正常に完了したタスクしか表示されません。それでは、例外があればそれを渡します。
return LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
throw t.Exception.InnerException;
}
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
素晴らしい、今これは実際にうまくいく。一つの商品に。それでは、そのループはどうでしょうか。結局のところ、元の同期バージョンのロジックと同等の解決策は、次のようになります。
Task AsyncLoop() {
return AsyncLoopTask().ContinueWith(t =>
Counter.Value = t.Result,
TaskScheduler.FromCurrentSynchronizationContext());
}
Task<int> AsyncLoopTask() {
var tcs = new TaskCompletionSource<int>();
DoIteration(tcs);
return tcs.Task;
}
void DoIteration(TaskCompletionSource<int> tcs) {
LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
tcs.TrySetException(t.Exception.InnerException);
} else if (t.Result.Contains("target")) {
tcs.TrySetResult(t.Result.Length);
} else {
DoIteration(tcs);
}});
}
あるいは、上記すべての代わりに、asyncを使用して同じことを実行できます。
async Task AsyncLoop() {
while (true) {
string result = await LoadNextItem();
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
}
それは今もっとずっといいですね。
Wait
2番目の例で呼び出すそれから2つのスニペットは(ほとんど)同等です。 - ServygetWebPage
メソッドは両方のコードで使用できません。最初のコードではTask<string>
2番目にある間に戻り型string
戻り型そのため、基本的にあなたのコードはコンパイルされません。 - 正確に言えば。 - Royi Namir