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();
特定の状況では、どちらかが他方よりも好まれるのでしょうか?
ベストアンサー1
以下は、非同期解決を使用した場合の違いとさまざまな問題を説明するために最近使用したコード スニペットのシーケンスです。
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;
}
}
}
今ではずっと良くなりましたね。