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

时间:2013-06-10 11:59:40

标签: c# asynchronous async-await

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

3 个答案:

答案 0 :(得分:3)

MSDN docs和我的introductory async blog post都回答了这个问题。

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

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

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

答案 1 :(得分:1)

  

[...]暂停的位怎么能恢复生机?

使用委托(取自msdn)。

  

“await”关键字告诉编译器插入一个可能的   暂停/恢复指向标记为“异步”的方法。

     

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


  

单个线程是否需要返回轮询代码的暂停状态以查看它是否已完成?

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

答案 2 :(得分:0)

我实际上并不清楚具体如何在C#中实现await,但让我提出一种可以的方式,只是为了回答“它怎么可能?”的问题。 (而不是“如何实施”?)。

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

await也必须如此,其中C#编译器不会简单地为每行C#代码发出一堆简单的步骤。例如,它可以做的是使用await进行调用,生成调用给定方法的委托,然后将方法代码的 rest 捆绑为回调函数代表。

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

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