そのフラグメントを次のようにリファクタリングできます。
async Task<bool> WaitForItToWork()
{
bool succeeded = false;
while (!succeeded)
{
// do work
succeeded = outcome; // if it worked, make as succeeded, else retry
await Task.Delay(1000); // arbitrary delay
}
return succeeded;
}
どうやら、それが与える唯一の利点は、スレッドプールをより効率的に使用することです。これは、遅延を発生させるのに常にスレッド全体が必要であるとは限らないためです。
の取得方法によっては、outcome
を使用してこの仕事を完了するためのはるかに効率的な方法がある場合がありますasync/await
。GetOutcomeAsync()
多くの場合、自然な方法で Web サービス、データベース、またはソケット呼び出しを非同期に行うようなものがあるかもしれませんvar outcome = await GetOutcomeAsync()
。
WaitForItToWork
コンパイラによって部分に分割され、await
行からの部分が非同期で続行されることを考慮することが重要です。これがおそらく、内部でどのように行われるかについての最良の説明です。問題は、通常、コードのある時点で、非同期タスクの結果を同期する必要があるということです。例えば:
private void Form1_Load(object sender, EventArgs e)
{
Task<bool> task = WaitForItToWork();
task.ContinueWith(_ => {
MessageBox.Show("WaitForItToWork done:" + task.Result.toString()); // true or false
}, TaskScheduler.FromCurrentSynchronizationContext());
}
あなたは単にこれを行うことができました:
private async void Form1_Load(object sender, EventArgs e)
{
bool result = await WaitForItToWork();
MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false
}
ただし、それはForm1_Load
非同期メソッドにもなります。
[アップデート]
以下は、async/await
この場合に実際に何が行われるかを説明するための私の試みです。同じロジックの 2 つのバージョンWaitForItToWorkAsync
( を使用async/await
) とWaitForItToWorkAsyncTap
( を使用せずにTAP パターンasync/await
を使用) を作成しました。最初のバージョンは、2 番目のものとは異なり、非常に簡単です。したがって、async/await
主にコンパイラの構文糖衣ですが、非同期コードの記述と理解がはるかに簡単になります。
// fake outcome() method for testing
bool outcome() { return new Random().Next(0, 99) > 50; }
// with async/await
async Task<bool> WaitForItToWorkAsync()
{
var succeeded = false;
while (!succeeded)
{
succeeded = outcome(); // if it worked, make as succeeded, else retry
await Task.Delay(1000);
}
return succeeded;
}
// without async/await
Task<bool> WaitForItToWorkAsyncTap()
{
var context = TaskScheduler.FromCurrentSynchronizationContext();
var tcs = new TaskCompletionSource<bool>();
var succeeded = false;
Action closure = null;
closure = delegate
{
succeeded = outcome(); // if it worked, make as succeeded, else retry
Task.Delay(1000).ContinueWith(delegate
{
if (succeeded)
tcs.SetResult(succeeded);
else
closure();
}, context);
};
// start the task logic synchronously
// it could end synchronously too! (e.g, if we used 'Task.Delay(0)')
closure();
return tcs.Task;
}
// start both tasks and handle the completion of each asynchronously
private void StartWaitForItToWork()
{
WaitForItToWorkAsync().ContinueWith((t) =>
{
MessageBox.Show("WaitForItToWorkAsync complete: " + t.Result.ToString());
}, TaskScheduler.FromCurrentSynchronizationContext());
WaitForItToWorkAsyncTap().ContinueWith((t) =>
{
MessageBox.Show("WaitForItToWorkAsyncTap complete: " + t.Result.ToString());
}, TaskScheduler.FromCurrentSynchronizationContext());
}
// await for each tasks (StartWaitForItToWorkAsync itself is async)
private async Task StartWaitForItToWorkAsync()
{
bool result = await WaitForItToWorkAsync();
MessageBox.Show("WaitForItToWorkAsync complete: " + result.ToString());
result = await WaitForItToWorkAsyncTap();
MessageBox.Show("WaitForItToWorkAsyncTap complete: " + result.ToString());
}
スレッド化について一言。ここで明示的に作成された追加のスレッドはありません。内部的には、実装はプール スレッドを使用する場合があります (タイマー キューTask.Delay()
を使用すると思われます) が、この特定の例 (WinForms アプリ) では、後の継続は同じ UI スレッドで発生します。他の実行環境 (コンソール アプリなど) では、別のスレッドで続行される場合があります。IMO、 Stephen Cleary によるこの記事は、スレッド化の概念を理解するために必読です。await
async/await