在使用async/await
创建简单示例期间,我发现,一些示例只是在Button1_Click
类似方法上说明模式,并直接从async
方法自由更新GUI控件。因此可以将此视为安全机制。但是我的测试代码在TargetInvocationException
中的mscorlib.dll
例外不断崩溃,内部异常如:NullReference
,ArgumentOutOfRange
等。关于堆栈跟踪,一切似乎都指向{ {1}}标签显示结果(并直接从绑定到按钮事件处理程序的WinForms.StatusStrip
方法驱动)。在访问GUI控件时使用旧学校async
时,崩溃似乎已得到解决。
问题是:我错过了重要的事情吗?异步方法是否与先前用于长期操作的线程/后台工作程序相同,因此Control.Invoke
是推荐的解决方案?直接从Invoke
方法驱动GUI的代码片段是否错误?
编辑: 对于失踪来源的downvoters: 创建一个包含三个按钮的简单表单和一个包含两个标签的StatusStrip ...
async
答案 0 :(得分:6)
Task
和await
都不会在这方面给予任何保证。您需要考虑创建任务的上下文以及发布延续的位置。
如果您在winforms事件处理程序中使用await
,则会捕获同步上下文,并且continuation将返回到UI线程(事实上,它几乎调用了Invoke
给定的代码块)。但是,如果您只是使用Task.Run
启动新任务,或者从另一个同步上下文启动await
,则此操作不再适用。解决方案是在winforms同步上下文中运行适当的任务调度程序上继续运行。
但是,应该注意的是,它仍然不一定意味着async
事件将正常工作。例如,Winforms还将事件用于CellPainting
之类的事情,它实际上取决于它们同步运行。如果你在这样的事件中使用await
,它几乎可以保证不能正常工作 - 延续仍将发布到UI线程,但这并不一定能使它安全。例如,假设控件具有如下代码:
using (var graphics = NewGraphics())
{
foreach (var cell in cells)
CellPainting(cell, graphics);
}
当你的继续运行时,graphics
实例已经完全被处理掉了。甚至可能该单元格不再是控件的一部分,或控件本身不再存在。
同样重要的是,代码可能取决于您的代码更改内容 - 例如,您在其EventArgs
中设置了一些值的事件,以指示例如成功,或给予一些回报价值。同样,这意味着你不能在里面使用await
- 就调用者所知,函数只是在你执行await
时返回(除非它同步完成)。
答案 1 :(得分:0)
由于您使用的是异步方法,我猜测您尝试执行的代码不在UI线程上。
看看这里: SO Question