近期項目中遇到了一個處理速度慢阻塞用戶界面操作的問題,因此想用多線程來解決。
在處理數據的循環中,新建線程,在新建的線程中處理數據。多線程同一時候處理數據,以此來達到加速的目的,使用戶界面操作變得流暢。
在多任務操作系統中。我們能夠在操作系統的協調下同一時候進行多個任務。各個任務以分時復用的形式來進行工作。
Windows操作系統通過進程ID來管理各進程。每一個進程至少包括一個線程。線程是進程中能夠獨立執行的程序片段。在主程序執行時,主程序能夠啟動線程。線程與主程序同一時候執行。
線程是系統中分時處理的最小單位,也就是說線程能夠與主程序並行執行。參與同分時處理。線程有自己獨立的棧處理數據。它在與主程序同一時候執行時能夠共享主程序定義的變量、函數。僅僅有當線程執行結束才把控制權還給主程序。
創建線程最直接的方法是創建新的線程類實例,並使用Address Of語句為執行的過程傳遞托付。
可是這樣的方法不能傳遞參數和返回值。
我們能夠通過將在單獨的線程中執行的過程包裝到類或結構中。為它們提供參數,並使之能返回參數。
以下是一個新建線程的小demo:
Class TasksClass Public StrArg As String Public RetVal As Boolean Public resultFlag As Boolean Public Event ResultEvent(ByVal resultFlag As Boolean) Public Sub SomeTask() ' 將 StrArg 字段用作參數。 MessageBox.show("StrArg 包括字符串" & StrArg) RetVal = True ' 設置返回參數的返回值。End Sub End Class
Public Class Test Private Sub Test_Load(sender As Object, e As EventArgs) Handles MyBase.Load DoWork() End Sub ' 要使用類。請設置存儲參數的屬性或字段, ' 然后。依據須要異步調用方法。Sub DoWork() Dim Tasks As New TasksClass() Dim Thread1 As New System.Threading.Thread(AddressOf Tasks.SomeTask) Tasks.StrArg = "參數A" ' 設置用作參數的字段。 Thread1.Start() ' 啟動新線程。 Thread1.Join() ' 等待線程 1 執行結束。
' 顯示返回值。
MessageBox.show("線程 1 返回值" & Tasks.RetVal) End Sub End Class
這樣一個線程就被新建出來了。
可是其實。創建過多的線程反而會影響性能。由於各個線程之間是分時復用的,操作系統須要在它們之間不斷的切換。線程過多的時候,大量的時間消耗在線程切換上。
所以須要控制線程的數量。
如果我們僅僅新建10個線程,剩余的數據等待這10個線程中的某個結束,再繼續新建線程處理數據。
使線程的總數一直保持在10個。
Public Class ThreadingObj Public paper As WorkSpace Public Sub ThreadingInsertPaper() '多線程處理數據 End Sub End Class Private PaperList As List(Of WorkSpace) = New List(Of WorkSpace) Dim ThreadingList As List(Of System.Threading.Thread) = New List(Of System.Threading.Thread)
For Each paper In PaperList '多線程處理PaperList If ThreadingList.Count < 10 Then '不到10個線程則繼續新起線程 Dim ThreadingObject As New ThreadingObj Dim ThreadingTask_1 As New System.Threading.Thread(AddressOf ThreadingObject.ThreadingInsertPaper) ThreadingObject.paper = paper ThreadingList.Add(ThreadingTask_1) ThreadingTask_1.Start() ' 啟動新線程。 Else Dim goOnFlag As Boolean = False '循環等待有線程結束 Do If CheckThreadingStatus() Then '存在已完畢的線程 If ThreadingList.Count <= 10 Then Dim ThreadingObject As New ThreadingObj Dim ThreadingTask_1 As New System.Threading.Thread(AddressOf ThreadingObject.ThreadingInsertPaper) ThreadingObject.paper = paper ThreadingList.Add(ThreadingTask_1) ThreadingTask_1.Start() ' 啟動新線程。 goOnFlag = True End If Else '全部線程都在進行中 End If Loop Until goOnFlag = True End If Next
Function CheckThreadingStatus() As Boolean '返回True表示存在已完畢的線程 If ThreadingList.Count <= 10 Then For Each ThreadingTaskItem In ThreadingList 'ThreadingTaskItem.IsAlive If ThreadingTaskItem.IsAlive = False Then ThreadingTaskItem.Abort() ThreadingList.Remove(ThreadingTaskItem) Return True End If Next End If Threading.Thread.Sleep(100) If ThreadingList.Count <= 10 Then For Each ThreadingTaskItem In ThreadingList If ThreadingTaskItem.IsAlive = False Then Return True End If Next End If Return False End Function
如果操作系統中有50個活躍的線程。如果我們的程序僅僅有一個線程。那么程序就占用了系統資源的五十分之中的一個。如果此時再多開一個線程,那么就占用了系統資源的2/51。我們的程序占用的資源越多。處理速度也就會更快一些。
由於各個線程之間是分時復用的,所以活躍的線程數量也會影響程序的效率。
有一種說法是2N + 2個線程數。是最有效率的。N是CPU的核數。
可是這一說法也一直存在爭議。
我的測試結果例如以下(我的電腦是i7 4770 。四核八線程):
線程數 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 |
耗時 | 753 | 409 | 345 | 316 | 305 | 286 | 280 | 282 | 273 | 273 | 264 | 266 |
測試的結果有點奇怪。。
。
。
。。
。
可能跟一些其它的因素也有關系。
。
。
比方DB的狀態,比方瀏覽網頁對CPU的占用