我正在尝试将我的程序变成多线程应用程序,但是我遇到了一些障碍,我在下面的代码中记录了这些障碍。我可以获得任何帮助以使其正常运行的帮助,因此我可以将此存根扩展为现有应用程序的更高效版本。
感谢您对此事的任何建议。 - 亚伦
Imports System.Threading
Public Class frmMain
''' <summary>Initializes the multithreaded form</summary>
Private Sub Initialize() Handles MyBase.Load
AddThread(AddressOf Update_UI)
running = True
For Each Thread In ThreadPool
Thread.IsBackground = True
Thread.Start()
Next
End Sub
''' <summary>Terminates the multithreaded form</summary>
Protected Overrides Sub Finalize() Handles MyBase.FormClosing
running = False
For Each Thread In ThreadPool
Thread.Join()
Thread = Nothing
Next
End Sub
''' <summary>Adds a worker thread to the ThreadPool</summary>
''' <param name="pointer">The AddressOf the function to run on a new thread.</param>
Private Sub AddThread(ByRef pointer As System.Threading.ParameterizedThreadStart)
Dim newthread As Integer
If ThreadPool Is Nothing Then newthread = 0 Else newthread = ThreadPool.GetUpperBound(0) + 1
ReDim Preserve ThreadPool(newthread)
ThreadPool(newthread) = New Thread(pointer)
End Sub
''' <summary>Updates the User Interface</summary>
Private Sub Update_UI()
'HELP: The commented out lines in this subroutine make the program work incorrectly when uncommented.
'HELP: It should echo 'output' to the titlebar of frmMain, but it also makes the form unresponsive.
'HELP: When I force the form to quit, the 'termination alert' does not trigger, instead the application hangs completely on Thread.Join (see above).
'HELP: If I remove DoEvents(), the form is unable to be closed...it simply goes unresponsive. Shouldn't the multithreading keep us from needing DoEvents()?
'If Me.InvokeRequired Then
' Me.Invoke(New MethodInvoker(AddressOf Update_UI))
'Else
While running
Dim output As String = System.DateTime.Now + " :: Update_UI() is active!"
Debug.Print(output)
'Application.DoEvents()
'Me.Text = output
End While
Debug.Print(System.DateTime.Now + " :: Termination signal recieved...")
'End If
End Sub
Delegate Sub dlgUpdate_UI()
Private ThreadPool() As Thread
Private running As Boolean
End Class
答案 0 :(得分:2)
是的,您尝试过的任何内容都无法正常运行。您正确地确定需要使用Control.Invoke()在主线程上运行Me.Text分配。这就是出错:
请考虑使用BackgroundWorker类,它会处理其中的一些细节。
答案 1 :(得分:1)
这是do while循环,它会消耗你所有的循环,所以无论你使用多少线程,你都会失去战斗并保持处理器忙。以下内容将更适合您要实现的目标。
Imports System.Threading
Public Class Form1
Private t As New Timer(AddressOf DoTimer, Nothing, 1000, 1000)
Private Sub DoTimer(ByVal state As Object)
UpdateUi()
End Sub
''' <summary>Updates the User Interface</summary>
Private Sub UpdateUi()
If InvokeRequired Then
Invoke(New DlgUpdateUi(AddressOf UpdateUi))
Else
Dim output As String = DateTime.Now & " :: Update_UI() is active!"
Debug.Print(output)
Text = output
End If
End Sub
Delegate Sub DlgUpdateUi()
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
t.Dispose()
End Sub
End Class
答案 2 :(得分:0)
如果我曾说过一次我说了一百万次。使用Invoke
虽然在许多情况下很有用,但却被滥用并过度使用。如果你想要做的只是向用户显示工作线程的进度,那么使用Invoke
并不总是最好的选择。它看起来也不是最好的选择。
而是将您分配给output
的状态文本发布到可以通过UI线程访问的变量中。然后使用System.Windows.Forms.Timer
以更合理的速率定期轮询其值...可能每1秒钟左右。 Tick
事件已在UI线程上运行,因此您可以立即开始使用此值通过操作各种UI控件向最终用户显示。
字符串很容易从线程传递到线程,因为它们是不可变的,这意味着它们本身就是线程安全的。您唯一需要担心的是确保UI线程看到发布到共享变量的最新引用。在C#中,您可以使用volatile
关键字。在VB中,您可以使用UI线程中的Thread.VolatileRead
和工作线程中的Thread.VolatileWrite
。当然,如果您更愿意将SyncLock
中的读写包装起来也是完全可以接受的。