您可以像这样重构该片段:
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
在这种情况下实际执行的操作。我创建了相同逻辑的两个版本,WaitForItToWorkAsync
(使用async/await
)和WaitForItToWorkAsyncTap
(使用不带的TAP 模式async/await
)。与第二个不同,第一个版本非常简单。因此,虽然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()
实现可能使用池线程(我怀疑他们使用Timer Queues),但在这个特定的示例(一个 WinForms 应用程序)中,之后的延续await
将发生在同一个 UI 线程上。在其他执行环境(例如控制台应用程序)中,它可能会在不同的线程上继续。IMO, Stephen Cleary 的这篇文章async/await
是理解线程概念的必读文章。