为什么这个多线程VB.NET(2010 Express Edition)程序不能正常工作?

时间:2011-09-11 05:19:25

标签: vb.net multithreading debugging vb.net-2010

我正在尝试将我的程序变成多线程应用程序,但是我遇到了一些障碍,我在下面的代码中记录了这些障碍。我可以获得任何帮助以使其正常运行的帮助,因此我可以将此存根扩展为现有应用程序的更高效版本。

感谢您对此事的任何建议。 - 亚伦

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

3 个答案:

答案 0 :(得分:2)

是的,您尝试过的任何内容都无法正常运行。您正确地确定需要使用Control.Invoke()在主线程上运行Me.Text分配。这就是出错:

  • 您的Invoke()调用使整个方法在主线程上运行。它将开始执行循环并永不退出。你的表格变得紧张,因为它不再做任何其他事情,比如重新绘制标题栏以显示更改的文本或回应用户输入
  • DoEvents调用使表单恢复活动但现在您遇到了一个新问题:用户可以关闭窗口并且代码保持运行。 运行标志永远不会设置为false,因此程序不会停止。用户界面已经消失了。代码通常会在ObjectDisposedException上炸弹,但在特定情况下不会,Text属性存储在私有变量中
  • 您可以更改代码,以便只在主线程上运行Me.Text分配。但是现在你遇到了一个新问题:主线程将受到调用请求的攻击,并且不再需要执行常规(低优先级)任务。它变得紧张。基本问题是您尝试更快地更新标题栏方式。没有意义,用户无法快速阅读。每秒更新20次就足够了,人眼看起来很顺利
  • 不要对此类任务使用Finalize()方法,代码可以轻松触发2秒终结器线程超时,轰炸您的程序。

请考虑使用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中的读写包装起来也是完全可以接受的。