从异步方法更新GUI

时间:2015-08-12 13:02:44

标签: c# winforms asynchronous thread-synchronization

在使用async/await创建简单示例期间,我发现,一些示例只是在Button1_Click类似方法上说明模式,并直接从async方法自由更新GUI控件。因此可以将此视为安全机制。但是我的测试代码在TargetInvocationException中的mscorlib.dll例外不断崩溃,内部异常如:NullReferenceArgumentOutOfRange等。关于堆栈跟踪,一切似乎都指向{ {1}}标签显示结果(并直接从绑定到按钮事件处理程序的WinForms.StatusStrip方法驱动)。在访问GUI控件时使用旧学校async时,崩溃似乎已得到解决。

问题是:我错过了重要的事情吗?异步方法是否与先前用于长期操作的线程/后台工作程序相同,因此Control.Invoke是推荐的解决方案?直接从Invoke方法驱动GUI的代码片段是否错误?

example

编辑: 对于失踪来源的downvoters: 创建一个包含三个按钮的简单表单和一个包含两个标签的StatusStrip ...

async

2 个答案:

答案 0 :(得分:6)

Taskawait都不会在这方面给予任何保证。您需要考虑创建任务的上下文以及发布延续的位置。

如果您在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