首先,让我们澄清一些术语:“异步”(async)意味着它可能会在调用线程开始之前将控制权交还给调用线程。在async 方法中,那些“屈服”点是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 方法,该方法可以返回给它的调用者而不是阻塞”,答案是声明方法 async 并使用 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”。这些“等待对象”可以是其他 async 方法或只是返回等待对象的常规方法。返回Task/Task<T>的常规方法可以使用Task.Run在后台线程上执行代码,或者(更常见的是)它们可以使用TaskCompletionSource<T>或其快捷方式之一(TaskFactory.FromAsync 、Task.FromResult 等)。我不建议将整个方法包装在 Task.Run 中;同步方法应该有同步签名,是否应该包装在Task.Run中应该由消费者决定:
private int DoWork()
{
return 1 + 2;
}
private void MoreSynchronousProcessing()
{
// Execute it directly (synchronously), since we are also a synchronous method.
var result = DoWork();
...
}
private async Task DoVariousThingsFromTheUIThreadAsync()
{
// I have a bunch of async work to do, and I am executed on the UI thread.
var result = await Task.Run(() => DoWork());
...
}
我的博客上有async/await intro;最后是一些很好的后续资源。 async 的 MSDN 文档也非常好。