从 WinForm 到 WPF 再到 UWP,我们能看到微软在 .Net 设计方面的进步。多线程处理所需编写的代码量越来越少。我以 Visual Basic 为例,介绍两种最常用、最简单易用的的多线程处理方法。
函数不需要在执行过程中返回结果或进度
可以使用 Await 和 Async 关键字。用 Async 修饰一个函数,即可将其标记为异步函数。调用时在函数名前加 Await,就可以在保持 UI 线程相应的情况下,等待异步函数完成执行。请看例子:
Public Async Function HeavyWorkAsync() As Task(Of String) Dim web As New Net.WebClient Await web.DownloadStringAsync("http://some.thing") Await Task.Run( Sub() System.Threading.Thread.Sleep(5000) End Sub) Return "Done" End Function Private Async Sub btn_Click(sender As Object, e As EventArgs) btn.IsEnabled = False btn.Content = "Wait" Dim ret As String ret = Await HeavyWorkAsync() btn.Content = ret btn.IsEnabled = True End Sub
在按钮被点按后,在执行到 ret = Await HeavyWork() 时,UI 线程会继续响应,但是函数会等待异步操作执行完后继续进行。
正如你所见,每个异步函数的名字会以 “Async” 结尾,同时每个异步函数中至少包含一个 Await 语句。如果异步返回值,那他会返回一个 Task(Of 异步函数需要返回值的类型) 。 这些正是一个异步函数所必要的部分。
如果你要进行的费时步骤提供了异步函数,那就使用 Await DoSomethingAsync() 。如果没有提供异步函数,那就放到 Await Task.Run() 块中。
有关 Async / Await 异步编程的详细内容,请参阅:
https://docs.microsoft.com/zh-cn/dotnet/visual-basic/programming-guide/concepts/async
函数需要在执行过程中返回值或进度
这种情况可以使用事件。可以看下面的例子:
' 主窗体 Public Class MainWindow Private WithEvent web As New WebOps ' 省略创建线程来执行 WebOps 类 GetResponse() 方法的内容 Private Sub ProgressUpdateHandler(value As Integer) Handles web.ProgressUpdate Me.Dispatcher.Invoke(New Action( Sub() progressBar.Value = value End Sub) End Sub End Class ' 类 Public Class WebOps Public Event ProgressUpdate(value As Integer) Public Sub GetResponse() ' 执行一些步骤... RaiseEvent ProgressUpdate(35) ' 执行另一些步骤... RaiseEvent ProgressUpdate(65) End Sub End Class
创建一个进度更新的事件,通过在一个方法内多次触发,并在 UI 线程内创建事件处理函数,即可在函数在另一个线程内执行时,即时返回执行进度。
当然还可以创建一个执行完后触发的事件,在执行完成后触发。