Vb.net 异步/等待按钮单击块UI
我有一个简单的异步按钮和一个异步/等待函数。当我点击我的按钮时,我的界面冻结了。谁能解释一下我做错了什么Vb.net 异步/等待按钮单击块UI,vb.net,Vb.net,我有一个简单的异步按钮和一个异步/等待函数。当我点击我的按钮时,我的界面冻结了。谁能解释一下我做错了什么 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 F
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
专用异步函数ComputeText()作为任务(字符串的)
将结果变暗为字符串=等待任务。运行(函数()
《线程睡眠》(2000)
尺寸i为整数=0
而我呢
i+=1
“由于其他员工也可以从外部访问它,所以请阻止
同步锁定此锁定
LabelCounter.Invoke(Sub()
LabelCounter.Text=i
末端接头)
端同步
结束时
返回“完成”
终端功能)
返回结果
端函数
当我点击我的按钮时,我的界面冻结了
正如其他人所指出的,即使您使用的是Async
/wait
(这是正确的),问题在于调用
Invoke
将中断UI线程并告诉它做些什么(在本例中,更新标签)。问题是后台线程正在以100000倍的速度中断UI
第二个难题是UI消息的优先级WM_PAINT
(即,“重新绘制屏幕上需要更改的部分”)的优先级低于“运行此任意代码”消息。因此,用户界面实际上没有时间刷新,因为后台Invoke
请求具有优先级,而且它们进入的速度太快
我建议使用标准的IProgress
方法来报告进度,而不是更新标签和直接使用Invoke
。这样做的好处是将UI显示与程序逻辑(在后台线程中运行)分离;此外,它还使用框架无关的方法(IProgress
)而不是将您的逻辑专门绑定到WinForms(Invoke
)
唯一真正的解决方案是限制后台更新。您可以在后台逻辑本身中执行此操作(即,仅每1000个周期左右发布一次更新),但这并不理想,因为1)这是业务逻辑中要解决的UI问题;2)它可以根据系统使用情况、CPU功率等随时间变化
因此,我建议让后台逻辑始终发送更新(使用IProgress.Report
),并让IProgress
实现对其进行限制。这最容易通过Rx实现。Lee Campbell有一个(这是更合适的解决方案),我有一个(这还没有更新到更合适的程度,但很容易设置一个预先限制的进度)
当我点击我的按钮时,我的界面冻结了
正如其他人所指出的,即使您使用的是Async
/wait
(这是正确的),问题在于调用
Invoke
将中断UI线程并告诉它做些什么(在本例中,更新标签)。问题是后台线程正在以100000倍的速度中断UI
第二个难题是UI消息的优先级。WM_PAINT
(即,“重新绘制需要更改的屏幕部分”)的优先级低于“运行此任意代码”因此,用户界面实际上没有时间刷新,因为后台Invoke
请求占据优先权,而且它们进入的速度太快
我建议使用标准的IProgress
方法来报告进度,而不是更新标签和直接使用Invoke
。这样做的好处是将UI显示与程序逻辑(在后台线程中运行)分离;此外,它还使用框架无关的方法(IProgress
)而不是将您的逻辑专门绑定到WinForms(Invoke
)
唯一真正的解决方案是限制后台更新。您可以在后台逻辑本身中这样做(即,仅每1000个周期左右发布一次更新),但这并不理想,因为1)这是您的业务逻辑中要解决的UI问题;2)它可以根据系统使用情况、CPU功率等随时间变化
因此,我建议让后台逻辑始终发送更新(使用IProgress.Report
),并让IProgress
实现阻止更新。这是最容易通过接收完成。Lee Campbell有一个(这是更合适的解决方案),我有一个(尚未更新为更合适的解决方案,但很容易设置一个预先限制的进度)。您使用的是Invoke
,这是一个同步调用。嗯,调用Invoke
十万次对性能来说可能不是最好的。@Glorin ah ok,这就是为什么它的frezzes mu UI(不知道Invoke是同步的)。但首先我必须更新这个标签,所以我必须使用Invoke(因为我在不同的线程上,所以我还必须进行同步锁定,因为我还有其他任务在更新标签。所以必须这样做,对吗?有没有其他方法可以实现?您使用的是Invoke
,这是一个同步调用。那么,调用Invoke
十万次可能是n。)ot将是性能最好的。@Glorin啊,好吧,这就是为什么它的frezzes mu UI(不知道调用是同步的)