Async / await按钮单击阻止UI

时间:2016-10-26 16:25:08

标签: vb.net

我有简单的异步按钮和async / await功能。当我点击我的按钮时,我的UI会冻结。谁能向我解释我做错了什么?

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    LabelCounter.Text = "Running"
    LabelCounter.Text = await ComputeText()
End Sub

和这个功能:

Private Async Function ComputeText() As Task(Of String)
    Dim result as string = Await Task.Run(Function()
                                              'Thread.Sleep(2000)
                                              Dim i as Integer = 0
                                              While i <> 100000
                                                  i += 1
                                                  'block as other staff can access it as well from outside
                                                  SyncLock thisLock                                                   
                                                      LabelCounter.Invoke(Sub()
                                                                              LabelCounter.Text = i 
                                                                          End Sub)
                                                  End SyncLock
                                              End While

                                              Return "Done"
                                          End Function)
    Return result
End Function

1 个答案:

答案 0 :(得分:0)

  

当我点击我的按钮时,我的UI会冻结

正如其他人所说,即使您正在使用Async / Await(这是正确的),问题仍然是Invoke

Invoke将中断UI线程并告诉它做某事(在这种情况下,更新标签)。问题是后台线程正在尽可能快地中断UI 100,000次。

第二个难题是UI消息优先WM_PAINT(即#34;需要更改屏幕的重绘部分)的优先级低于&#34;运行此任意代码&#34;信息。因此,用户界面实际上没有时间进行刷新,因为后台Invoke请求具有优先权,并且它们进入的速度太快。

我建议使用标准IProgress<T>方法报告进度,而不是直接更新标签和使用Invoke。这样做的好处是可以将UI显示与程序逻辑分开(在后台线程中运行);此外,它使用与框架无关的方法(IProgress<T>),而不是将您的逻辑特定地绑定到WinForms(Invoke)。

唯一真正的解决方案是限制后台更新。您可以在后台逻辑本身中执行此操作(即,仅每1000个周期左右发布一次更新),但这并不理想,因为1)它是您的业务逻辑中解决的UI问题; 2)它可以根据系统使用情况,CPU功率等随时间变化

因此,我建议让后台逻辑总是发送更新(使用IProgress<T>.Report),并让IProgress<T> 实现限制它们。这最容易通过Rx完成。 Lee Campbell有blog post on the subject(这是更合适的解决方案),我有一个down-and-dirty git-er-done approach in a gist(尚未更新为更合适,但设置起来很容易)预先限制的进展。)