user3363744
user3363744

Reputation: 169

VB Simple Threading using Delegates

I understand the concept of threading. I understand the concept of delegates but I am having trouble combining the two concepts. I followed a tutorial and I was able to make two counters start at the same time using multiple threads on my form. I was getting the cross threading error and I used the Me.CheckForIllegalCrossThreadCalls = False work around. I know my current method isnt ideal and I was wondering how I would use delegates to produce the same results. I have been at it all day and still cant seem to grasp the idea. How would I add delegates to the code below to allow two counters to work simultaneously on my form?

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

I would love to see the code using delegates but what I would really like is to have the understanding of whats going on.

Thanks guys

Upvotes: 0

Views: 1117

Answers (3)

NoviceProgrammer
NoviceProgrammer

Reputation: 3365

using Me.CheckForIllegalCrossThreadCalls = False is not the right approach.

Basically, Cross-thread operation not valid exception is raised when a control is being updated from a thread other than the thread it was created on.

Each control exposes a InvokeRequired property that allows it to be updated in a thread-safe manner.

Therefore the right way to update the label is to use code like -

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 will execute the method on a thread pool thread. Once the method returns, the thread is returned to the pool.

So basically instead of starting a new thread, you will asynchronously execute the method using Delegate.BeginInvoke

Upvotes: 1

One should use Control.Invoke to execute a specified delegate on the thread that owns the control's underlying window handle. Also, replace Me.Refresh() with Thread.Sleep(1) to ensure that other threads get some execution time.

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

Here's an example.

'                                                 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

Upvotes: 1

Taugenichts
Taugenichts

Reputation: 1365

Not sure if this is exactly what you're looking for, but here's my best shot at it:

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

Sorry I have the first and second parts reversed and it's a console app instead of a form, but I figured the important part was to demonstrate delegates...

As a note, I'm not sure how familiar you are with delegates, but I included the EndInvoke to make sure the program wouldn't terminate prior to the delegates finishing their operations. They are used to return any values or exceptions from the method call as well as making the program wait. (In this case, since it's a sub there is no return value, so I didn't bother worrying about it)

Upvotes: 1

Related Questions