我把一個小測試程序放在一起,瞭解VB.net中的多線程。經過大量的研究,嘗試和失敗,我得到了我的程序運行,至少有一點。爲什麼我的任務不能並行運行?
現在它的表現很奇怪,我無法解釋爲什麼。也許你可以告訴我,我的程序出了什麼問題,爲什麼它的行爲方式和/或我必須改變的方式才能使它按照本應該工作的方式工作。
我的程序包含一個類(TestClass),它模擬我想要在不同線程上執行的長時間運行的任務,進度窗體(Form1)顯示來自長時間運行的任務和主程序的進度消息,將這兩者放在一起。
這裏是我的代碼:
Public Class Form1
Public Sub AddMessage1(ByVal message As String)
If String.IsNullOrEmpty(message) Then
Exit Sub
End If
Me.ListBox1.Items.Add(message)
Me.ListBox1.SelectedIndex = Me.ListBox1.Items.Count - 1
Application.DoEvents()
End Sub
Public Sub AddMessage2(ByVal message As String)
If String.IsNullOrEmpty(message) Then
Exit Sub
End If
Me.ListBox2.Items.Add(message)
Me.ListBox2.SelectedIndex = Me.ListBox2.Items.Count - 1
Application.DoEvents()
End Sub
End Class
的TestClass:
Imports System.Threading
Public Class TestClass
Public Event ShowProgress(ByVal message As String)
Private _milliSeconds As UShort
Private _guid As String
Public Sub New(milliSeconds As UShort, ByVal guid As String)
_milliSeconds = milliSeconds
_guid = guid
End Sub
Public Function Run() As UShort
For i As Integer = 1 To 20
RaiseEvent ShowProgress("Run " & i)
Thread.Sleep(_milliSeconds)
Next i
Return _milliSeconds
End Function
End Class
Main過程包含兩個列表框(ListBox1中和ListBox2)
進展形式(Form1中) :
Imports System.Threading.Tasks
Imports System.ComponentModel
Public Class Start
Private Const MULTI_THREAD As Boolean = True
Public Shared Sub Main()
Dim testClass(1) As TestClass
Dim testTask(1) As Task(Of UShort)
Dim result(1) As UShort
testClass(0) = New TestClass(50, "Test1")
testClass(1) = New TestClass(200, "Test2")
Using frm As Form1 = New Form1
frm.Show()
AddHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
AddHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
If MULTI_THREAD Then
testTask(0) = Task(Of UShort).Factory.StartNew(Function() testClass(0).Run, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext)
testTask(1) = Task(Of UShort).Factory.StartNew(Function() testClass(1).Run, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext)
Task.WaitAll(testTask)
result(0) = testTask(0).Result
result(1) = testTask(1).Result
Else
result(0) = testClass(0).Run
result(1) = testClass(1).Run
End If
RemoveHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
RemoveHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
frm.Close()
End Using
MessageBox.Show("Result 1: " & result(0) & "; Result 2: " & result(1))
End Sub
End Class
這個程序應該打開的進度表,並行運行兩個長時間運行的任務,表現出的兩項長時間運行的任務的進度消息,關閉進度表格當兩個長時間運行的任務都完成,顯示消息框與長時間運行任務的結果。
現在到我無法解釋的行爲:如果我運行此程序,它顯示在兩個列表框中的「運行1」,因此兩個任務開始運行,但只有第一個列表框被填充,並且只有當第一個列表框被填充(第一個任務完成),第二個列表框繼續填充。所以,這兩項任務都開始了,但第二項任務在繼續運行之前等待第一項任務完成。
但是,當我在我的主程序中註釋掉Task.WaitAll(testTask)
時,這是相反的。它在兩個列表框中顯示「運行1」,然後第二個列表框被填充,並且僅當第二個列表框被填充(第二個任務完成)時,第一個列表框繼續運行。
爲什麼我的程序行爲如此奇怪,我該如何讓它同時運行我的兩個任務?
謝謝你的幫助解決這個一點神祕:-)
更新
由於唯一的答案迄今指出,它與我使用我刪除了SynchronizationContext
做從我的主要過程的代碼:
Main過程:
Imports System.Threading.Tasks
Imports System.ComponentModel
Public Class Start
Private Const MULTI_THREAD As Boolean = True
Public Shared Sub Main()
Dim testClass(1) As TestClass
Dim testTask(1) As Task(Of UShort)
Dim result(1) As UShort
testClass(0) = New TestClass(50, "Test1")
testClass(1) = New TestClass(200, "Test2")
Using frm As Form1 = New Form1
frm.Show()
AddHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
AddHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
If MULTI_THREAD Then
testTask(0) = Task(Of UShort).Factory.StartNew(Function() testClass(0).Run)
testTask(1) = Task(Of UShort).Factory.StartNew(Function() testClass(1).Run)
Task.WaitAll(testTask)
result(0) = testTask(0).Result
result(1) = testTask(1).Result
Else
result(0) = testClass(0).Run
result(1) = testClass(1).Run
End If
RemoveHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
RemoveHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
frm.Close()
End Using
MessageBox.Show("Result 1: " & result(0) & "; Result 2: " & result(1))
End Sub
End Class
爲了避免因爲非法跨線程調用的InvalidOperationException
,我改變形式的代碼如下:
進展形式(Form1中)含有兩個列表框(ListBox1中和ListBox2):
Public Delegate Sub ShowProgressDelegate(ByVal message As String)
Public Class Form1
Public Sub AddMessage1(ByVal message As String)
If String.IsNullOrEmpty(message) Then
Exit Sub
End If
Debug.Print("out List 1: " & message)
If Me.InvokeRequired Then
Me.BeginInvoke(New ShowProgressDelegate(AddressOf Me.AddMessage1), message)
Else
Debug.Print("in List 1: " & message)
Me.ListBox1.Items.Add(message)
Me.ListBox1.SelectedIndex = Me.ListBox1.Items.Count - 1
Application.DoEvents()
End If
End Sub
Public Sub AddMessage2(ByVal message As String)
If String.IsNullOrEmpty(message) Then
Exit Sub
End If
Debug.Print("out List 2: " & message)
If Me.InvokeRequired Then
Me.BeginInvoke(New ShowProgressDelegate(AddressOf Me.AddMessage2), message)
Else
Debug.Print("in List 2: " & message)
Me.ListBox2.Items.Add(message)
Me.ListBox2.SelectedIndex = Me.ListBox2.Items.Count - 1
Application.DoEvents()
End If
End Sub
End Class
我剛剛添加了一些Debug.Prints用於調試和InvokeRequired
部件。
現在這兩個任務並行運行(我知道是因爲Debug.Print
調用了「out List」消息),但是我的進度消息沒有顯示在列表框中,相應的代碼從未得到執行(Debug.Print
調用「在列表中「消息不會顯示在調試窗口中)。
我以正確的方式執行InvokeRequired
部分的搜索結果,而且我的做法看起來是正確的,但它不起作用。
有人可以告訴我,有什麼不對?
再次感謝您的幫助。
如果我不在創建任務時使用'TaskScheduler.FromCurrentSynchronizationContext'並在我的表單的代碼中使用InvokeRequired',程序根本沒有顯示任何進度,表格只打開並顯示等待光標,就是這樣。這是我第一個問題的主題,如何在不阻塞UI線程的情況下運行任務。 – Nostromo 2013-04-11 07:14:06
我已經嘗試過'BackgroundWorker'並且運行了它,但是 1.我不想使用BackgroundWorker,因爲它是一個組件,因此需要一個表單才能正常工作,並且此表單不可重用,因爲所有代碼和事件處理髮生在表單中。 2.我想了解多線程,因此想要了解爲什麼它按照它的方式工作,即使它不是我希望它工作的方式。 那麼,有什麼建議怎麼做才能讓我的兩個'任務'並行運行**和**顯示進度? – Nostromo 2013-04-11 07:21:36
@Nostromo - 如果你使用'InvokeRequired'(和'Invoking'),但你的UI線程仍然在WaitAll()調用中,那麼不,它不會工作 - UI線程*不能*調用'WaitAll()' - 它必須可以自由地處理'Invoke'請求。 – 2013-04-11 07:23:46