在Visual Basic中,异步调用和事件处理通常有不同的实现方式,取决于语言版本和应用类型。在经典的WinForms VB.NET应用中,事件队列的处理通常涉及调用Application.DoEvents()。该方法允许事件处理器“释放控制权”,以便其他消息可以被处理,而不阻塞主线程:
While loading Application.DoEvents() '允许UI响应用户操作 End While
对于在.NET中进行异步任务执行,VB.NET使用:
BackgroundWorker — WinForms组件,用于将长时间操作转移到单独的线程,并安全地与UI线程交互。Async/Await — 便捷的现代异步系统,自.NET 4.5以来出现,允许像“同步”一样编写异步代码:Public Async Function LoadDataAsync() As Task Dim result As String = Await GetWebDataAsync() TextBox1.Text = result End Function
区别:
DoEvents仅处理UI消息队列,并不创建新线程。BackgroundWorker将工作推送到一个单独的线程,并提供事件以安全地返回到UI线程。Async/Await实现异步性,同时封装了对UI线程的控制权返回。在循环中使用
DoEvents的危险是什么,如果需要从磁盘加载文件并更新ProgressBar?
回答:
DoEvents暂时将控制权传递给Windows的其他事件,但并不会释放线程。如果在繁重的循环中使用它(例如,同时读取大文件并更新接口),可能会出现意外的错误:用户事件和鼠标事件将被处理,这可能导致处理的重复启动或应用程序“挂起”。如果预计会有重负载—应该使用BackgroundWorker或Task。
错误方法示例:
For i = 1 To 1000000 ProgressBar1.Value = i / 10000 Application.DoEvents() '不会释放主线程! Next
历史
DoEvents。结果是接口“挂起”:如果用户再次点击“打开”按钮—第二个文件读取过程就开始了,与第一个混合,导致数据扭曲和应用程序崩溃。历史
DoEvents来更新窗体上的图表,因为他不熟悉BackgroundWorker。在更新巨大数据点集时,应用程序开始卡顿,随后因Windows消息队列溢出而“崩溃”。历史
我们使用了通过Async/Await的异步调用,但没有意识到长时间的工作不能在“热”UI线程中启动而不使用Await Task.Run(...)。结果:接口对点击没有反应,进度条没有更新,因为长时间的方法仍然在主线程上执行。