VB.net のマルチスレッドについて学習するための小さなテスト プログラムを作成しました。多くの調査、試行、失敗の後、少なくとも少しはプログラムを実行することができました。
今では奇妙な振る舞いをしており、その理由を説明することはできません. 私のプログラムの何が問題なのか、なぜそのように動作するのか、および/または動作するはずの方法で動作させるために何を変更する必要があるのか を教えてください。
私のプログラムには、それぞれ別のスレッドで実行する長時間実行タスクをシミュレートするクラス (TestClass)、長時間実行タスクからの進行状況メッセージを表示する進行状況フォーム (Form1)、およびこれら 2 つを配置するメイン プロシージャが含まれています。一緒。
これが私のコードです:
2 つのリストボックス (ListBox1 と ListBox2) を含む進行状況フォーム(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
テストクラス:
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
主な手順:
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
このプログラムは、進行状況フォームを開き、2 つの長時間実行タスクを並行して実行し、2 つの長時間実行タスクの進行状況メッセージを表示し、両方の長時間実行タスクが終了したら進行状況フォームを閉じ、メッセージ ボックスに結果を表示することになっています。長時間実行されるタスク。
ここで説明できない動作について: このプログラムを実行すると、両方のリストボックスに「Run 1」が表示されるため、両方のタスクが実行を開始しましたが、最初のリストボックスのみがいっぱいになり、最初のリストボックスがいっぱいになったときにのみ (最初のタスクが完了した場合)、2 番目のリストボックスは引き続き入力されます。したがって、両方のタスクが開始されますが、2 番目のタスクは最初のタスクが終了するのを待ってから実行を続けます。
しかし、Task.WaitAll(testTask)
メイン プロシージャで をコメント アウトすると、逆になります。両方のリストボックスに「Run 1」が表示され、2 番目のリストボックスがいっぱいになり、2 番目のリストボックスがいっぱいになったとき (2 番目のタスクが完了したとき) にのみ、最初のリストボックスが実行され続けます。
プログラムの動作がおかしいのはなぜですか? また、2 つのタスクを同時に実行するにはどうすればよいですか?
この小さな謎を解決するためにあなたの助けをありがとう:-)
更新これまでの唯一の回答は、私が使用
しているものに関係していると述べているため、メインプロシージャのコードから削除しました:SynchronizationContext
主な手順:
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
に、フォームのコードを次のように変更しました。
2 つのリストボックス (ListBox1 と ListBox2) を含む進行状況フォーム(Form1):
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
「in List」メッセージを含む呼び出しは実行されません)。 t はデバッグ ウィンドウに表示されます)。
その部分を行う正しい方法をグーグルで検索しましたが、その方法は正しいようですが、InvokeRequired
うまくいきません。
誰か教えてください、何が悪いのですか?
またよろしくお願いいたします。