C#等待运算符和async关键字

示例

await运算符和async关键字组合在一起:

必须使用async关键字修改使用await的异步方法。

相反的情况并不总是正确的:您可以将方法标记为在其主体中async不使用await。

await实际要做的是暂停代码执行,直到等待的任务完成为止。任何任务都可以等待。

注意:您无法等待不返回任何内容(无效)的异步方法。

实际上,“ suspends”一词有点误导,因为不仅执行停止,而且线程可以腾出空间来执行其他操作。在后台,它await是由一些编译器魔术实现的:它将方法分为两部分-之前和之后await。当等待的任务完成时,将执行后一部分。

如果我们忽略了一些重要的细节,则编译器会为您大致执行以下操作:

public async Task<TResult> DoIt()
{
    // do something and acquire someTask of type Task<TSomeResult>  
    var awaitedResult = await someTask;
    // ...做更多事情并产生TResult类型的结果
    return result;
}

变成:

public Task<TResult> DoIt()
{
    // ...
    return someTask.ContinueWith(task => {
        var result = ((Task<TSomeResult>)task).Result;
        return DoIt_Continuation(result);
    });
}

private TResult DoIt_Continuation(TSomeResult awaitedResult)
{
    // ...
}

任何常用的方法都可以通过以下方式转换为异步方法:

await Task.Run(() => YourSyncMethod());

当您需要在UI线程上执行长时间运行的方法而不冻结UI时,这可能是有利的。

但是这里有一个非常重要的说明:异步并不总是意味着并发(并行甚至多线程)。即使在单个线程上,async-await仍然允许异步代码。例如,请参阅此自定义任务计划程序。这样的“疯狂”任务计划程序可以简单地将任务转换为在消息循环处理中调用的功能。

我们需要自问:哪个线程将执行方法的延续DoIt_Continuation?

默认情况下,await运算符使用当前的同步上下文安排执行继续的时间。这意味着默认情况下,WinForms和WPF延续在UI线程中运行。如果由于某种原因需要更改此行为,请使用method :Task.ConfigureAwait()

await Task.Run(() => YourSyncMethod()).ConfigureAwait(continueOnCapturedContext: false);