近期項目中遇到了一個處理速度慢阻塞用戶界面操作的問題,因此想用多線程來解決。
在處理數據的循環中,新建線程,在新建的線程中處理數據。多線程同一時候處理數據,以此來達到加速的目的,使用戶界面操作變得流暢。
在多任務操作系統中。我們能夠在操作系統的協調下同一時候進行多個任務。各個任務以分時復用的形式來進行工作。
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的占用
