什么线程在`await`关键字后运行代码?
我先发一个简单的例子:
private void MyMethod() { Task task = MyAsyncMethod(); task.Wait(); } private async Task MyAsyncMethod() { //Code before await await MyOtherAsyncMethod(); //Code after await }
假设我在单线程应用程序中运行上述代码,就像控制台应用程序一样。 我很难理解代码//Code after await
的代码是如何运行的。
据我所知,当我在MyAsyncMethod()
控件中点击await
关键字时,会返回到MyMethod()
,但后来我用task.Wait()
锁定了该线程。 如果线程被锁定,那么如果应该接受它的线程被锁定,那么//Code after await
运行//Code after await
怎么办?
是否创建了一个新线程来//Code after await
? 或者主线程是否神奇地走出task.Wait()
运行//Code after await
?
我不确定这是怎么回事?
如果从主线程调用,则在Winform App中发布的代码将为“死锁”,因为您使用Wait()
阻止主线程。
但在控制台应用程序中这是有效 但是怎么样?
答案隐藏在SynchronizationContext.Current
。 await
捕获“SynchronizationContext”,当任务完成时,它将继续在相同的“SynchronizationContext”中。
在winform应用程序中, SynchronizationContext.Current
将被设置为WindowsFormsSynchronizationContext
,它将发布到“消息循环”的调用,但是谁将要处理它? out主线程在Wait()
中Wait()
。
在控制台应用程序中,默认情况下不会设置SynchronizationContext.Current
,因此当没有“SynchronizationContext”可用于等待捕获时它将为null
,因此它将调度继续到ThreadPool
(TaskScheduler.Default,这是ThreadpoolTaskScheduler),因此等待后的代码工作(通过线程池线程)。
可以使用Task.ConfigureAwait(false);
来控制上述捕获行为Task.ConfigureAwait(false);
这将阻止winform应用程序死锁,但await
后的代码不再在UI线程中运行。
是否创建了一个新线程来等待?
也许。 也许不吧。 Task
的等待模式实现使用在await表达式开始时为“current”的同步上下文运行continuation( await
表达式之后的位)。 例如,如果您处于UI线程的上下文中,则意味着您将最终返回到相同的UI线程。 如果你是一个线程池线程,你最终会回到一些线程池线程,但它可能是一个不同的线程。
当然,使用您的代码示例,如果您在UI线程中,您对Wait()
调用将阻止UI线程,以便继续无法运行 – 您需要注意这一点。 (对您不知道要完成的任务调用Wait()
或Result
,以及哪些可能需要在当前线程上工作,这是一个坏主意。)
请注意,您可以调用Task.ConfigureAwait
以便表达不需要继续在相同上下文中的意图。 这通常适用于不关心它们运行在哪个线程上的库方法:
await task.ConfigureAwait(false);
(它影响的不仅仅是线程 – 它是捕获与否的整个上下文。)
我认为通过等待来熟悉引擎盖下的内容是个好主意。 网上有很多文档,如果你能给我一个简短的插件,那么还有第三版的C#深度 ,以及关于这个主题的Tekpub截屏系列 。 或者从MSDN开始,然后从那里开始。