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

问题描述 投票:0回答:3

我已经使用这段代码有一段时间了:

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 来调用

这是由我的线程调用的,(它可以工作(线程部分 - 顺便说一句,这很棒),但有一个小缺陷,即不执行执行步骤 - 或我在 Private Sub setForm1PrgBarPerformStep() 中编码的任何其他 UI 更改

这有充分的理由吗?

vb.net multithreading delegates invoke
3个回答
3
投票

当你的线程第一次点击 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

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


0
投票

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

其次,您不应该在此示例中创建显式线程。从 .Net 4 开始,您应该改用任务。要像使用 Thread.Sleep 那样模拟长时间运行的操作,请使用 Task.Delay(200),然后对其进行 Await 或显式调用 .Wait。请记住在后台线程上安排延迟并委托回来进行 UI 更新。


0
投票

好的,感谢本节中提出的评论以及一些研究的帮助,我终于弄清楚了。

我有点作弊,通过运行 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

这就是全部了:) - 希望这可以帮助某人,并感谢所有人帮助我完成我的 - 有点多线程,是的......我知道有更好的方法,但这个对我有用:)

© www.soinside.com 2019 - 2024. All rights reserved.