当异步代码尝试在已经执行的线程上恢复时会发生什么?
我觉得这个问题的答案是由于我对线程如何工作有一个错误的概念,但是这里有。
private void button1_Click(object sender, EventArgs e) { this.TestMethodAsync(); // No await, ie fire and forget // ** Some code here to perform long running calculation (1) ** } private async Task TestMethodAsync() { // Some synchronous stuff await Task.Delay(1000); // ** Some code here to perform long running calculation (2) ** }
首先,我不会“发射并忘记”这样的异步方法(我会使用Task.Run),但我遇到的代码确实如此,我试图了解效果是什么。
在使用WindowsFormsSynchronizationContext
的WinForms应用程序中,我对async和await的理解告诉我,当我单击button1时,该方法将在UI线程上同步启动。 它将调用TestMethodAsync
并同步运行,直到它到达await。 然后它将捕获上下文,启动Task.Delay
任务,并将控制权交给调用者。 由于我们没有等待此调用,因此button1_Click
将在UI线程上继续并开始执行计算(1)。
在某些时候, Task.Delay(1000)
将完成。 然后,继续将使用捕获的上下文运行TestMethodAsync
方法的其余部分,在这种情况下,意味着将继续在UI线程上运行。 现在开始执行计算(2)。
我们现在有两个独立的代码段,希望同时在同一个线程(UI线程)上运行。 我对此的调查似乎表明,线程在两段代码之间来回切换,以便同时执行它们。
题:
我对这里到底发生了什么感到困惑。 如何在已经运行其他代码的线程上恢复? 是什么迫使线程在想要运行的两段代码之间切换? 通常,当您尝试在已经运行其他代码的线程上恢复时会发生什么?
(我想这与我的点击事件在UI线程上的运行方式没有任何不同,因为我知道它在UI线程上运行,我知道UI线程也在做其他事情,但是我之前没想过这样。)
这是你不明白的秘密:我给你Windows消息循环
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; BOOL bRet; while(TRUE) { bRet = GetMessage(&msg, NULL, 0, 0); if (bRet <= 0) break; TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
这是您申请的实际“主要”; 你只是看不到它,因为它隐藏在幕后。
无法想象一个更简单的循环。 它从队列中获取消息。 如果没有更多消息,则必须完成该程序。 如果有消息,则它运行标准消息转换并调度消息,然后继续运行。
如何在已经运行其他代码的线程上恢复?
事实并非如此。 “在运行其他代码的线程上恢复”实际上是在队列中放入一条消息 。 DispatchMessage正在同步调用“其他代码”。 完成后,它返回循环,轮询队列,消息指示接下来需要分派的代码。 然后它会同步运行,直到它返回循环。
是什么迫使线程在想要运行的两段代码之间切换?
没有。 这不会发生。
通常,当您尝试在已经运行其他代码的线程上恢复时会发生什么?
描述需要运行哪个continuation的消息排队等候。
我想这与我的点击事件首先在UI线程上运行的方式没有任何不同,因为我知道它在UI线程上运行,我知道UI线程也在做其他事情,但我以前没有像这样想过它。
开始考虑它。
点击事件完全相同。 你的计划正在做点什么; 你点击鼠标; 点击处理程序不会中断UI线程并开始在其上运行新工作。 相反,消息排队,当您的UI线程控件返回到消息循环时,最终会处理单击; DispatchMessage导致通过Windows窗体中的某种机制调用Button1_OnClick。 这就是WinForms的意义所在 ; 用于将Windows消息转换为对C#方法的调用的机制。
但你已经知道了 。 您知道,当事件驱动程序执行长时间运行的同步操作时,UI会冻结,但最终会处理该单击事件。 你是怎么想的那样发生的? 您必须在某种程度上理解他们排队等待以后处理,对吧?
练习: DoEvents
做什么?
练习:鉴于您现在所知道的:如果您在循环中调用DoEvents
以取消阻止您的UI,可能会出现什么问题?
练习:如何await
GUI应用程序中的DoEvents
?
如何在已经运行其他代码的线程上恢复?
它需要专门设计来支持它。 需要有一些框架允许线程接受工作,然后在稍后的某个时间执行该工作。
这是您的UI线程的工作方式。 它有一个队列,每当你在UI线程中安排工作时,你就会在队列的末尾添加一个项目。 然后,UI线程从队列中获取第一个项目,执行它,然后在完成后继续执行下一个项目,依此类推,直到您结束应用程序。
是什么迫使线程在想要运行的两段代码之间切换?
没什么,因为它没有那样做。 它运行一个,然后当它完成时,它运行另一个。
通常,当您尝试在已经运行其他代码的线程上恢复时会发生什么?
要么有人写了一些自定义代码来专门做到这一点,在这种情况下,它会执行任何专门告诉它的代码,否则你不能。
- 如何从Entity Framework / LINQ中的特定表中提取元组列表?
- DropCreateDatabaseIfModelChanges EF6导致System.InvalidOperationException:支持上下文的模型已更改
- 如何在.Net中获得video缩略图?
- 如何修复“请求的资源正在使用中。 (HRESULTexception:0x800700AA)“
- 有没有更好的方法在C#中自定义SOAP头?
- .Net中当前CultureInfo的距离单位
- 如何将标签的字体颜色设置为与GroupBox的标题颜色相同?
- 使用C#打开Word文档(.doc)会因文件阻止设置而导致COMException
- MVC3 DropDownList + ViewBag问题