如何在VB.NET中与线程中的UI进行正确交互

时间:2015-11-09 05:49:32

标签: vb.net multithreading

我是VB.NET线程的新手

至于简单测试,我尝试了以下操作,我需要使用值平滑地填充列表框。 但它没有像我预期的那样工作,它挂起了界面。请让我知道我在这里做错了什么。

谢谢。

Imports System.Threading

Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim Thr As Threading.Thread
    Thr = New Threading.Thread(New Threading.ThreadStart(AddressOf tprocess))
    'Thr.SetApartmentState(ApartmentState.STA)
    Thr.IsBackground = True
    Thr.Start()        
End Sub

Private Delegate Sub DoStuffDelegate()

Private Sub tprocess()

    Dim i As Integer

    For i = 0 To 20000
        If Me.InvokeRequired Then
            Me.Invoke(New DoStuffDelegate(AddressOf tprocess))
        Else
            ListBox1.Items.Add(i)
        End If
    Next

End Sub

End Class

3 个答案:

答案 0 :(得分:1)

当您编写代码来创建线程时,您总是要担心线程可能导致的错误类型。它们非常难以诊断,解决它们的唯一方法就是知道它们存在并仔细编写代码,以便知道如何避免它们。

最常见的线程错误是线程争用,死锁和firehose错误。您的代码中有第1个和第3个错误。你在抱怨第三个。很快:线程竞争错误正在使用Me.InvokeRequired。当Me.Invoke()语句执行时,您无法保证它仍然是 true 。当用户在线程仍在运行时关闭窗口时,这会出错。当您尝试解决此问题时,您将看到第二个错误的样子。但你还没到那里。

firehose错误是Me.Invoke()调用。非常快,工作线程的工作时间不到一微秒,你以很高的速度完成20000次。然而,另一个线程实际上必须完成添加项目UI线程的工作。这是快,它不仅需要添加项目,还需要重新绘制控件。很多微秒。

当这种情况发生时,你的UI线程正在燃烧100%核心,试图跟上无情的调用请求率。尽可能努力地将项目添加到列表框中。有些东西必须给予,而它正在这样做,它没有照顾它必须做的低优先级的工作。绘画并响应用户输入。实际上,您的UI看起来完全冻结了。你不能再看到它了,并试图关闭窗口不起作用。 实际上已经死了,它很难发挥作用。

需要一段时间,可能是几秒钟,给予或接受。直到工作线程完成其for()循环并停止使用调用请求猛击UI线程。一切都恢复正常。

像这样的firehose bug是非常基础的。解决它的唯一方法是减少调用Invoke()或降低速率。注意如何在Invoke()调用之后立即修复Thread.Sleep(50)。但当然这会大大减慢你的工作线程。通过使用AddRange()而不是Add(),一次添加(比方说)1000个项目,可以减少调用Invoke()的频率。哪个是正确的修复,但现在仍然尝试从工作线程更新列表框变得毫无意义。也可以通过 AddRange()调用来完成。最快捷的方式。

答案 1 :(得分:0)

尝试更改:

Thr = New Threading.Thread(New Threading.ThreadStart(AddressOf tprocess))

到此:

Thr = New Threading.Thread(AddressOf tprocess)

ThreadStart将立即启动该线程

答案 2 :(得分:0)

我尝试了以下方式。我几乎很容易处理。 Backgroudworker完美地处理了这种情况。

Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object,ByVal e As System.ComponentModel.DoWorkEventArgs)处理BackgroundWorker1.DoWork

    Dim i As Integer

    For i = 1 To 20000
        BackgroundWorker1.ReportProgress((i / 20000) * 100, i)
        Threading.Thread.Sleep(1)
    Next
End Sub

Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    ProgressBar1.Value = e.ProgressPercentage
    ListBox1.Items.Add(e.UserState)
End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    MsgBox("Complete")
End Sub