这是我在这个论坛上的第一个问题,由于我不是以英语为母语的人,我希望你对我放轻松,以防我做错或说错了什么。
所以,这是我的问题:
我喜欢解决一个简单的问题(看起来如此),但我已经研究了一个多星期并尝试了不同的东西,但到目前为止都没有奏效,我总是偶然发现同样的问题。
我喜欢做的是打开一个进度表,然后开始两个长时间运行的任务(例如从两个数据库中获取数据)。这些长时间运行的任务向进度表报告他们的进度,完成后进度表关闭并打开另一个表单以显示两个长时间运行的任务的结果。在进度表关闭和程序继续(或关闭)之前,这两项任务都必须完成(或取消/失败)。
因此,进度表(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
If Me.InvokeRequired Then
Me.Invoke(New ShowProgressDelegate(AddressOf Me.AddMessage1), New Object() {message})
Else
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
If Me.InvokeRequired Then
Me.Invoke(New ShowProgressDelegate(AddressOf Me.AddMessage2), New Object() {message})
Else
Me.ListBox2.Items.Add(message)
Me.ListBox2.SelectedIndex = Me.ListBox2.Items.Count - 1
Application.DoEvents()
End If
End Sub
End Class
然后我有我的 TestClass 模拟长时间运行的任务并引发进度事件:
Imports System.Threading
Public Class TestClass
Public Event ShowProgress(ByVal message As String)
Private _milliSeconds As UShort
Public Sub New(milliSeconds As UShort)
_milliSeconds = milliSeconds
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)
testClass(1) = New TestClass(100)
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(0), testTask(1))
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
如果我将常量 MULTI_THREAD 设置为 false,则一切正常(但按顺序)。但是如果我将 if 设置为 true,它只会显示表单,但不会显示任何进度,也不会显示生成的消息框。如果我调试它永远不会到达 Task.WaitAll(...) 之后的行。
我已经尝试过其他多线程方法,例如使用普通线程(无返回值)、后台工作人员(仅百分比进度,无文本消息)、委托上的 BeginInvoke/EndInvoke,但没有任何效果,或者它显示出与上述相同的行为。
我在另一个论坛上被告知,Task.WaitAll 阻塞了 UI 线程,因为它是在 UI 线程上调用的,但我不敢相信,因为 MSDN 明确指出 Task.WaitAll 等待移交的任务完成或者失败,我没有交出 UI 线程。
所以,我不知道还能尝试什么,所以我希望你能向我指出我的错误,或者告诉我另一种方法来尝试解决我的小问题。
非常感谢您提前。
更新:
我做了更多的测试和调试。如果我将表单代码中的两个“Me.Invoke(...)”更改为“Me.BeginInvoke(...)”,那么我的表单中仍然没有显示任何进度消息,但至少任务有效我得到了我的关闭消息框。因此,进度消息的实际交付或显示似乎会导致问题。
也许这会触发你的想法?
再次感谢你。
更新二:
经过大量的试验和错误,它似乎工作,至少一点点。我在创建任务时交出了“TaskScheduler.FromCurrentSynchronizationContext”,而不是表单代码中的“Me.InvokeRequired”部分。所以我至少显示了我的进度消息,但任务仍然没有完全并行运行。但这是我下一个问题的主题。