我读过关于C#中的异步编程的文章,但我仍然不完全理解异步方法的延续是如何执行的。据我理解,异步编程与多线程无关。我们可以在UI上运行异步方法,稍后它将继续在该UI线程上运行(同时不阻塞并继续响应消息循环中的其他消息)。
这是大多数GUI应用程序的基本消息循环:
while (1)
{
bRet = GetMessage(&msg, NULL, 0, 0);
if (bRet > 0) // (bRet > 0 indicates a message that must be processed.)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
...DispatchMessage()调用UI事件处理程序。事件处理程序中的代码不应该阻塞主线程。因此,如果我们想要创建一个从磁盘加载大量数据的按钮,我们可以使用如下的异步方法:(简化伪代码)
public async Task ButtonClicked()
{
loadingBar.Show();
await AsyncLoadData();
loadingBar.Hide();
}当执行到达await AsyncLoadData();行时,它存储上下文并返回任务对象。DispatchMessage()完成,消息循环重复出现在bRet = GetMessage(&msg, NULL, 0, 0);行。
所以我的问题是,rest代码是如何执行的?异步操作是否会触发一条新消息,然后由DispatchMessage()再次处理?或者消息循环有另一个方法(分派后),它检查已完成的异步操作?
发布于 2020-12-17 13:37:17
所以我的问题是,rest代码是如何执行的?异步操作是否会触发一条新消息,然后由DispatchMessage()再次处理?或者消息循环有另一个方法(分派后),它检查已完成的异步操作?
默认情况下,await将捕获一个“上下文”,并使用该上下文恢复方法的执行。这个“上下文”是SynchronizationContext.Current,回到了TaskScheduler.Current。UI应用程序提供SynchronizationContext,例如WindowsFormsSynchronizationContext或DispatcherSynchronizationContext。当await完成时,它会将该方法的延续安排在该上下文上(在本例中,是在SynchronizationContext上)。
对于WinForms,Control.BeginInvoke,它将发布一条Win32消息,也就是由WinProc处理。
对于WPF,Dispatcher,其中将回调添加到调度队列。。这个队列也是由Win32 WinProc循环处理的。
发布于 2020-12-17 13:07:09
Alex在这方面写了一本很棒的书,它被称为“C# 5中的异步”,我强烈建议阅读它。我不能说这背后的低层次细节,但在高级别CLR会创建如下内容:
void __buttoncliked_remaining_code_1(...) {
loadingBar.Hide();
} 因此,将触发一个特定的事件,指示异步作业已经完成。然后,__buttoncliked_remaining_code_1()将像任何常规的C#函数一样,按时间顺序执行。CLR将用于任何线程,但它很可能会重用遇到的一个等待关键字,在您的例子中,这个关键字可能是GUI线程。
https://stackoverflow.com/questions/65340943
复制相似问题