异步如何只能在一个线程上工作

本文关键字:一个 线程 工作 异步 | 更新日期: 2023-09-27 18:17:48

如果一些代码'等待'并且单线程继续运行程序,那么暂停的位如何恢复生命?单线程是否需要回轮询暂停代码位的状态以查看其是否完成?

异步如何只能在一个线程上工作

MSDN文档和我的async入门博客文章都回答了这个问题。

总之,当一个async方法遇到一个尚未完成的操作的await时,默认情况下它将捕获当前上下文。这个"上下文"是当前的SynchronizationContext,除非它是null,在这种情况下它是当前的TaskScheduler。之后,当操作完成时,async方法的剩余部分被调度到该上下文中。

在UI应用程序中,这可以是UI SynchronizationContext(在UI线程上调度方法)。在ASP。. NET应用程序中,这通常是一个请求上下文(它没有绑定到特定的线程)。如果在线程池上运行async方法,它通常是线程池上下文(同样,不绑定到任何特定线程)。

回到UI的例子,UI线程确实有一个中央的"消息循环",它运行,处理Win32消息从它的队列。UI SynchronizationContext只是向队列发送一条消息,告诉UI线程执行async方法的下一部分。

[…暂停的部分如何恢复生命?

通过使用委托(取自msdn)

" await "关键字告诉编译器插入一个可能的挂起/恢复点到标记为" async "的方法。

逻辑上这意味着当你写" await someObject;编译器将生成代码来检查操作是否由someObject表示的已经完成。如果有,执行在等待点上同步继续。如果没有,那么生成的代码将把延续委托连接到等待对象对象,这样当表示的操作完成时将调用延续委托。这个延续委托将重新进入方法,在这个等待位置拾取先前的调用被中断。此时,无论是否被等待的对象在被等待的时候已经完成了将提取对象的结果,或者如果操作失败,发生的任何异常都将被传播。


单线程需要回轮询暂停位代码的状态,看看它是否完成?

不,没有这样的事。当你调用await时,来自当前线程的控制会在调用堆栈上走一步,即它被返回到包含await的方法的被调用方。当任务完成时,TPL调用委托方法,该方法从await行恢复控制。

我实际上不知道await具体是如何在c#中实现的,但让我提出一种可能的方式,只是为了回答这个问题"这是如何可能的?"(而不是"它是如何实现的"?)。

首先考虑yield关键字,这是完全不同的,但我认为,更普遍的理解。当您编写一个返回IEnumerable<T>并在函数体中使用yield return的函数时,我们大多数人现在都明白c#编译器为您生成一个迭代器类,并生成看起来与您编写的代码非常不同的IL。我的观点是,我们必须意识到c#代码本身并不总是非常清楚地映射到一组IL指令;有时转换是复杂的。

对于await也是这样,c#编译器不会简单地为每行c#代码发出一堆简单的步骤。它可以做的是,例如,使用await进行调用,生成调用给定方法的委托,然后将方法代码的rest捆绑为委托的回调。

其他方法如何异步操作与await关键字无关。它可能在一个单独的线程上执行工作,或者执行一些I/O操作。无论它做什么,它必须符合返回Task<T>的异步模式;这就是await所依赖的。

在IL中,结果函数在将剩余的逻辑添加为侦听器之后返回,以便在单独线程上发生的工作完成时接收信号。然后,该单独的线程将向原始线程(在大多数情况下,这实际上是一个消息循环)发送一条消息,并附上对侦听器的引用。