代码之家  ›  专栏  ›  技术社区  ›  Pakk

VB.net Windows窗体中的尝试-多线程

  •  1
  • Pakk  · 技术社区  · 11 年前

    一段时间以来,我一直在使用以下代码:

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim t2 As New Threading.Thread(AddressOf SetLabelText2)
        t2.IsBackground = True
        t2.Start()
    End Sub
    
    Delegate Sub UpdateDelegate()
    
    Private Sub SetLabelText2()
        If InvokeRequired Then
            Invoke(New UpdateDelegate(AddressOf SetLabelText2))
        Else
            For i = 1 To 110
                Label2.Text = Text
                Threading.Thread.Sleep(200)
            Next
        End If
    End Sub
    

    这段代码运行良好,只是它不是多线程的应用程序,并将其置于无响应状态。

    我的问题很简单, 1.我如何解决这个问题(我将要使用的应用程序将在

    If InvokeRequired Then
    
        Invoke(New UpdateDelegate(AddressOf SetLabelText2))
    
    Else
    
       FunctionCall1()
    
       FunctionCall2()
    
       FunctionCall3()
    
    End If
    

    他们都在工作,并占用了我的UI线程。

    我已经找了很长一段时间了,任何帮助都很感激。

    -编辑- 在线程中读取表单中的信息时也出现问题…:)谢谢你到目前为止的帮助

    编辑---

    好的,我已经更改了我的代码,使其看起来像

    Delegate Sub setForm1PrgBarPerformStepdelegate(s As Integer) 
        Private Sub setForm1PrgBarPerformStep(ByVal s As Integer)
            If Form1.ProgressBar1.InvokeRequired Then
                Dim d As New setForm1lblAlltextdelegate(AddressOf setForm1PrgBarPerformStep)
                Form1.Invoke(d, New Object() {s})
            Else
                If s = 1 Then
                Form1.ProgressBar1.PerformStep()
            Else
    
            End If
        End If
    End Sub
    

    使用setForm1PrgBarPerformStep(1)'或0调用这段代码

    Wich是由我的线程调用的,(它有效(线程部分-witch非常棒,btw),有一个小缺陷,就是没有执行步骤-或任何其他UI更改I在Private Sub-setForm1PrgBarPerformStep()中编码

    这有充分的理由吗?

    3 回复  |  直到 11 年前
        1
  •  3
  •   wborgsm    11 年前

    当线程第一次命中SetLabelText2时,InvokeRequired将为true,因此

    Invoke(New UpdateDelegate(AddressOf SetLabelText2))
    

    将被调用。

    现在,Invoke调用内部实际发生的情况是,您的线程将把委托作为任务传递给UI线程。因此,对SetLabelText2的第二个递归调用将由UI线程完成,而您的线程只是坐着等待UI线程从SetLabelText2返回。因此,您的UI会阻塞,因为UI线程将处于循环中,设置标签文本并休眠,并且没有机会处理消息/事件。

    作为一种解决方案,您可以在循环中调用Application.DoEvents(),而不是Sleep(200),但这可能会产生副作用,不建议这样做。

    更好的解决方案是将Invoke限制为严格必要的UI调用,在您的示例中:

    Private Sub SetLabelText2()
        For i = 1 To 110
            'Label2.Text = Text
            Invoke(MyDelegateThatDoesNothingButSettingTheLabelText)
            Threading.Thread.Sleep(200)
        Next
    End Sub
    

    现在,睡眠将由您的线程完成,并且您的用户界面保持响应。

        2
  •  0
  •   Jim Wooley    11 年前

    您的第一个问题是,由于Invoke,SetLabelText2中的Sleep是在UI线程上调用的,而不是在后台线程上调用。这将导致UI显示为锁定状态。新线程仅用于启动SetLabel,但工作几乎立即返回到调用线程。相反,您需要调用循环内部的后台任务,并为每次标签更新委派回UI。

    其次,在这个例子中,您不应该创建显式线程。从.Net 4开始,您应该使用Tasks。要模拟使用Thread.Sleep时长时间运行的操作,请使用Task.Delay(200),然后Await或call。显式等待。请记住在后台线程上安排延迟,并委托回UI更新。

        3
  •  0
  •   Pakk    11 年前

    好吧,多亏了本节的评论和一些研究的帮助,我终于明白了。

    我有点作弊,因为我运行UI线程,只为了在循环或短时间内完成繁重的工作,这里有一些代码-希望这能帮助其他人。

    Public t2 As Threading.Thread ' Instantiating the Thread. I do this in my Modules
                                  ' You will want to do this for all threads needed.
    
    'You would then go through your program like normal until you hit a heavy process eating point,
    'For me it was when i was converting images and sending them / moving them across the network
    
    'So to get what i wanted i used this in the middle of my public Function
    
        For Each s As String In sFiles
            c += 1 'This was just a simple file counter for the prg bar
    
    CheckAgain:
            If t3 Is Nothing Then
                t3 = New Thread(New ParameterizedThreadStart(AddressOf DoWork))
                t3.Start(s)
                GoTo NextOne
            ElseIf t4 Is Nothing Then
                t4 = New Thread(New ParameterizedThreadStart(AddressOf DoWork))
                t4.Start(s)
                GoTo NextOne
            ElseIf t5 Is Nothing Then
                t5 = New Thread(New ParameterizedThreadStart(AddressOf DoWork))
                t5.Start(s)
                GoTo NextOne
            ElseIf t6 Is Nothing Then
                t6 = New Thread(New ParameterizedThreadStart(AddressOf DoWork))
                t6.Start(s)
                GoTo NextOne
            ElseIf t7 Is Nothing Then
                t7 = New Thread(New ParameterizedThreadStart(AddressOf DoWork))
                t7.Start(s)
                GoTo NextOne
            ElseIf t8 Is Nothing Then
                t8 = New Thread(New ParameterizedThreadStart(AddressOf DoWork))
                t8.Start(s)
                GoTo NextOne
            ElseIf t9 Is Nothing Then
                t9 = New Thread(New ParameterizedThreadStart(AddressOf DoWork))
                t9.Start(s)
                GoTo NextOne
            Else
                GoTo CheckAgain
            End If
    NextOne:
        Next
    
    Sub DoWork(ByVal s As Object)
    
            ChangeCompression("Variables for my Function that did alot of work")
            'System.Threading.Thread.Sleep(10)
    
        'This is how i disposed of my threads
    
        If Not (t3 Is Nothing) Then
            t3 = Nothing
        ElseIf Not (t4 Is Nothing) Then
            t4 = Nothing
        ElseIf Not (t5 Is Nothing) Then
            t5 = Nothing
        ElseIf Not (t6 Is Nothing) Then
            t6 = Nothing
        ElseIf Not (t7 Is Nothing) Then
            t7 = Nothing
        ElseIf Not (t8 Is Nothing) Then
            t8 = Nothing
        ElseIf Not (t9 Is Nothing) Then
            t9 = Nothing
        End If
    
    End Sub
    

    这就是全部:)-希望这能帮助到别人,并感谢所有人帮助我完成我的多线程,是的。。。我知道有更好的方法,但这一种对我有效:)