使用委托的VB简单线程

时间:2014-03-19 06:57:15

标签: vb.net

我理解线程的概念。我理解代表的概念,但我在组合这两个概念时遇到了麻烦。我遵循了一个教程,我能够在我的表单上使用多个线程同时启动两个计数器。我收到了交叉线程错误,我使用了Me.CheckForIllegalCrossThreadCalls = False。我知道我当前的方法并不理想,我想知道如何使用委托来产生相同的结果。我整天都在这里,似乎仍然无法理解这个想法。如何将代理添加到下面的代码中以允许两个计数器同时在我的表单上工作?

Public Class Form1

   Dim i As Integer = 0
   Dim i2 As Integer = 0
   'declare two threads
   'thread 1
   Dim thread As System.Threading.Thread
   'thread 2
   Dim thread2 As System.Threading.Thread

   Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    'replace countup() with, this will assign the countup method to thread 1
    thread = New System.Threading.Thread(AddressOf countup)
    thread.Start()
End Sub

Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    ' countup2()
    thread2 = New System.Threading.Thread(AddressOf countup2)
    thread2.Start()
End Sub

Private Sub countup()
    Do Until i = 100000
        i = i + 1
        Label1.Text = i
        'We wont be able to see the label unless we refresh the form
        Me.Refresh()
    Loop
End Sub
Private Sub countup2()
    Do Until i2 = 100000
        i2 = i2 + 1
        Label2.Text = i2
        'We wont be able to see the label unless we refresh the form
        Me.Refresh()
      Loop
  End Sub

End Class

我很想看到使用代理的代码,但我真正想要的是了解最新情况。

谢谢你们

3 个答案:

答案 0 :(得分:1)

不确定这是否正是您正在寻找的,但这是我最好的拍摄:

Module Module1

Dim i As Integer = 0
Dim i2 As Integer = 0
Public Delegate Sub counting()
Sub Main()
    Dim del2 As counting = AddressOf countup2
    Dim callback2 As IAsyncResult = del2.BeginInvoke(Nothing, Nothing)
    Dim del1 As counting = AddressOf countup
    Dim callback1 As IAsyncResult = del1.BeginInvoke(Nothing, Nothing)
    del2.EndInvoke(callback2)
    del1.EndInvoke(callback1)
    Console.ReadLine()
End Sub

Private Sub countup()
    Do Until i = 100000
        i = i + 1
    Loop
    Console.WriteLine("i = " & i)
End Sub
Private Sub countup2()
    Do Until i2 = 100000
        i2 = i2 + 1
    Loop
    Console.WriteLine("i2 = " & i2)
End Sub
End Module

抱歉,我的第一和第二部分已经颠倒了,它是一个控制台应用而不是表格,但我认为重要的部分是展示代表......

作为一个说明,我不确定你对代表的熟悉程度,但我包括EndInvoke以确保程序在代表完成操作之前不会终止。它们用于从方法调用返回任何值或异常,以及使程序等待。 (在这种情况下,因为它是一个sub,没有返回值,所以我不用担心它)

答案 1 :(得分:1)

应该使用Control.Invoke在拥有控件底层窗口句柄的线程上执行指定的委托。另外,将Me.Refresh()替换为Thread.Sleep(1)以确保其他线程获得一些执行时间。

Private Sub countup()
    For i As Integer = 0 To 100000
        Me.Invoke(Sub() Me.Label1.Text = i.ToString())
        Thread.Sleep(1)
    Next
End Sub

这是一个例子。

'                                                 n=0       n=1
Private threads As Thread() = New Thread(2 - 1) {Nothing, Nothing}

Private Sub ButtonsClick(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click

    Dim n As Integer = -1 

    If (sender Is Me.Button1) Then
        n = 0 
    ElseIf (sender Is Me.Button2) Then
        n = 1 
    End If

    If (n <> -1) Then
        If (Me.threads(n) Is Nothing) Then
            'Start new thread.
            Me.threads(n) = New System.Threading.Thread(Sub() Me.CountUp(n))
            Me.threads(n).Start()
        Else
            'Abort thread.
            Me.threads(n).Abort()
            Me.threads(n) = Nothing
        End If
    End If

End Sub

Public Sub DisplayCount(n As Integer, text As String)
    'Inside UI thread.
    If (n = 0) Then
        Me.Label1.Text = text
    ElseIf (n = 1) Then
        Me.Label2.Text = text
    End If
End Sub

Private Sub CountUp(n As Integer)
    'Inside worker thread.
    Try
        If ((n < 0) OrElse (n > 1)) Then
            Throw New IndexOutOfRangeException()
        End If
        For i As Integer = 0 To 100000
            Me.Invoke(Sub() Me.DisplayCount(n, i.ToString()))
            Thread.Sleep(1)
        Next
    Catch ex As ThreadAbortException
        Me.Invoke(Sub() Me.DisplayCount(n, "Cancelled"))
        Thread.Sleep(1)
    Catch ex As Exception
        'TODO: Handle other exceptions.
    End Try
End Sub

答案 2 :(得分:1)

使用Me.CheckForIllegalCrossThreadCalls = False不是正确的方法。

基本上,当从创建它的线程以外的线程更新控件时,会引发跨线程操作无效异常。

每个控件都公开一个InvokeRequired属性,允许以线程安全的方式更新它。

因此更新标签的正确方法是使用像

这样的代码
Private Delegate Sub UpdateLabelDelegate(i As Integer)

Private Sub UpdateLabel(i As Integer)
        If Label1.InvokeRequired Then

            Dim del As New UpdateLabelDelegate(AddressOf UpdateLbl)
            Label1.Invoke(del, New Object() {i})
            'Me.Refresh()

        Else
            ' this is UI thread     
        End If
    End Sub

    Private Sub UpdateLbl(i As Integer)
        Label1.Text = i.ToString()
    End Sub

Delegate.BeginInvoke将在线程池线程上执行该方法。方法返回后,线程将返回池中。

所以基本上不是启动新线程,而是使用Delegate.BeginInvoke

异步执行方法