首先,让我们澄清一些术语:“异步”(async
)意味着它可能在开始之前将控制权还回给调用线程。在 async
方法中,这些“yield”点是 await
表达式。
这与多年来被 MSDN 文档误用为“在后台线程上执行”的术语“异步”非常不同。
更进一步混淆问题的是,async
与“可等待”非常不同;有一些返回类型不可等待的 async
方法,以及许多返回可等待类型但不是 async
的方法。
够说它们不是什么了;以下是它们是什么:
async
关键字允许异步方法(即允许 await
表达式)。async
方法可以返回 Task
、Task<T>
或(如果必须)void
。
- 遵循某种模式的任何类型都可以是可等待的。最常见的可等待类型是
Task
和 Task<T>
。
因此,如果我们将您的问题改为“如何以可等待的方式在后台线程上运行操作”,答案是使用 Task.Run
:
private Task<int> DoWorkAsync() // No async because the method does not need await
{
return Task.Run(() =>
{
return 1 + 2;
});
}
(但是这种模式是一个不好的方法;请参见下文。)
但是,如果你的问题是“如何创建一个async
方法,它可以向其调用者yield而不是阻塞”,那么答案是声明该方法为async
并在其“yielding”点使用await
:
private async Task<int> GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.example.com/");
return html.Length;
}
所以,事物的基本模式是使async
代码依赖于其await
表达式中的“awaitables”。这些“awaitables”可以是其他async
方法或只返回awaitable的常规方法。只返回Task
/Task<T>
的常规方法可以使用Task.Run
在后台线程上执行代码,或者(更常见)它们可以使用TaskCompletionSource<T>
或其快捷方式(TaskFactory.FromAsync
,Task.FromResult
等)。我不建议将整个方法包装在Task.Run
中;同步方法应具有同步签名,并且应该由消费者决定是否将其包装在Task.Run
中:
private int DoWork()
{
return 1 + 2;
}
private void MoreSynchronousProcessing()
{
var result = DoWork();
...
}
private async Task DoVariousThingsFromTheUIThreadAsync()
{
var result = await Task.Run(() => DoWork());
...
}
我在我的博客上写了一篇 async
/await
入门 的文章,在结尾处提供了一些好的后续资源。MSDN文档中有关于async
的内容也非常不错。