我理解线程的概念。我理解代表的概念,但我在组合这两个概念时遇到了麻烦。我遵循了一个教程,我能够在我的表单上使用多个线程同时启动两个计数器。我收到了交叉线程错误,我使用了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
我很想看到使用代理的代码,但我真正想要的是了解最新情况。
谢谢你们
答案 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