我看到你在你的问题上标记了multithreading,所以我认为重要的是要注意异步和多线程是两个完全不同的东西。通读 Microsoft 的文章 The Task asynchronous programming model in C#,了解它们的不同之处。关于烹饪的例子确实有助于阐明这一点。
当您调用异步方法时,任务(无论是什么)都会启动。但您通常也希望:
- 知道它已成功完成,或者
- 使用任务返回的结果(例如,如果它正在检索数据)
当事情变得有趣时,您如何以及何时这样做。异步编程的重点是允许线程在等待其他事情发生(网络请求、来自硬盘驱动器的数据等)的同时执行其他操作,而不是让线程闲置直到任务完成。
您可能已经体验过一些完全锁定的程序,并且在它执行某些操作时不允许您执行任何操作。这就是异步编程可以避免的。
在您的第一个示例中,您根本不需要等待结果。这通常被称为“一劳永逸”。您启动任务,然后代码执行立即移至下一行,您将永远不知道任务是否成功完成。
使用.Wait() 根本不是异步的。它将锁定线程,直到任务完成。更糟糕的是,当您尝试强制异步方法同步时,有时会导致应用程序无法恢复的死锁。搜索c# async wait deadlock,你会发现很多例子和解释。
理想情况下,您需要使用await。当你使用它时会发生这种情况:
- 方法 (
UploadFromStreamAsync) 被调用。
- 当异步操作开始时,
UploadFromStreamAsync 返回一个Task。
-
await 关键字将查看Task,如果它不完整,它将把当前方法的其余部分放在“待办事项”列表中,以供Task 完成并返回一个为调用方法添加新的Task。
只要您在调用堆栈中一直使用async,那么此时,线程就可以开始执行其“待办事项”列表中的其他事情。在 ASP.NET 中,它可能正在处理传入的新请求。在桌面应用程序中,它可能正在响应用户输入。这会发生在同一个线程上。
只要该任务完成,await 就会从 Task 中提取返回值。如果该方法只返回一个Task,那么这类似于void(没有返回值)。如果是Task<T>,则返回T类型的对象。然后您的代码在await 行之后恢复执行。
这听起来很复杂,但您真的不需要完全了解它在做什么,这是设计使然。 C# 的这一特性使您能够以与普通同步编程非常相似的方式使用异步编程。例如:
public async Task Upload() {
...
await blockBlob.UploadFromStreamAsync(arContents);
}
将与此完全相同:
public void Upload() {
...
blockBlob.UploadFromStream(arContents);
}
它们看起来非常相似,只是使用 async/await 会给您带来我上面提到的好处,而第二个则不会。