异步等待和线程

10

我正在使用 async-await 和任务,但是有一件事我不明白:

async 任务是否在单独的线程中执行?

根据 msdn 的说法 (异步编程):

async 和 await 关键字并不会创建额外的线程。异步方法不需要多线程,因为异步方法不在自己的线程上运行。

但是在 ThreadPool 类的描述中的备注中 (ThreadPool Class):

使用线程池线程执行操作的示例包括以下内容:

当您创建用于异步执行某些任务的 Task 或 Task 对象时,默认情况下任务将在线程池线程上计划运行。

所以,现在我不确定 async 任务是否使用单独的线程。请解释一下。谢谢。


有两种任务:委托任务(执行代码)和 Promise 任务(表示未来事件)。只有委托任务实际上会运行,它们可能在线程池上运行,也可能不在。更多信息请参见我的博客 - Stephen Cleary
3个回答

12

Task不一定代表一个额外的线程。

如果你await一个Task,你会将控制权返回给调用者,直到“某个人”将Task设置为已完成。

如果你通过Task.Run()Task.Factory.StartNew()开始一个Task,那么你传递给这些调用的操作会在另一个线程上执行(不是新线程,而是从线程池中取得的线程)。


1
事实上,它从未创建新的线程来运行任务,而总是使用从线程池中获取的线程,在超额订阅情况下,CLR 通过排队任务和节流来避免线程池中的超额订阅。 - Karolis Kajenas
1
@Carl 谢谢您的纠正,我已经更新了我的答案。 - René Vogt
假设代码(主线程)调用await asynctask1,并且假设asynctask1具有一些同步代码和一个等待asynctask2,那么在这种情况下,在等待asynctask1后控制权不会立即返回到主线程。但是当等待asynctask2时,控制权将返回到主线程。我理解得对吗? - variable

8
简单来说,任务可以在不同的线程上运行,也可以在调用者线程上运行。当调用者线程没有资源运行另一个任务时,任务将从线程池中获取一个线程。当调用者线程有足够的资源运行另一个任务时(空闲模式),任务将在调用者线程上运行。
与线程相比,任务是更高级的抽象——它表示可能或可能不由线程支持的并发操作。任务是可组合的(可以通过使用连续体将它们链接在一起)。它们可以使用线程池来减少启动延迟,并且通过TaskCompletionSource,它们可以利用回调方法,在等待I/O绑定操作时避免使用线程。
来源:Joseph Albahari、Ben Albahari的第565页《C# 5.0 in a Nutshell》。

@Gserg,你能提供证明否定这一点的来源吗? - Karolis Kajenas
这些问题的答案,以及与此问题链接的问题(https://dev59.com/gGYr5IYBdhLWcg3wvspA,https://dev59.com/AmMm5IYBdhLWcg3wX-LT)和链接到那里的资源。 - GSerg
@GSerg请查看更新的答案。引用来源:“C# 5.0 a Nutshell”第565页。 - Karolis Kajenas
@GSerg 我同意术语“load”并不适用于这种情况,是的(我已经更换了它)。但是引用清楚地指出它“可能没有由线程支持”。您能否详细说明我可能误解了哪个部分,并解释一下您的理解。 - Karolis Kajenas
“可能由线程支持,也可能不由线程支持的并发操作”意味着“异步操作可以作为单独的线程实现(即由单独的线程支持),也可以使用调用线程实现(即由当前线程支持)”。显然,您将“may not be backed by”理解为“当没有足够的支持时”,这并不是事实。该引用仅说明异步任务实际上并不关心线程,并且根据各种因素,它可能会或可能不会在单独的线程上启动。 - GSerg
显示剩余4条评论

7

async/await可以用于Tasks(准确来说,它适用于“可等待”对象,但是在本讨论中仅使用“Task”即可)。

Tasks表示“将在以后完成的工作单位”。这个工作单位可以是从线程池启动的新线程(通过Task.Run(),也可以是系统IO调用,如DbDataReader.ReadAsync(),或者是由TaskCompletionSource触发的事件。

我建议阅读Stephen Cleary的async和await介绍文章,以了解更多内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接