0

I have a MultiThreading issue and conceptual question using Visual Basic (but it applies to almost all languages). I have a function which:

  1. creates 5 instances of a class which each spawns a thread which calls a function
  2. The Function sleeps for X milli-seconds
  3. prints to console
  4. then terminates the thread

How can I ensure all instances of the class gets a chance to finish before main cuts it off??

Most relevant help through research: How do I pause main() until all other threads have died?

Here is my code:

Imports System
Imports System.Threading
Imports System.Collections.Generic
Module Module1
    Private raceTrack As New List(Of RaceCar)
    Sub Main()
        CreateRace() 
        'Main() would quit when the 5 car objects are created =[
        'How do I make sure Main will not exit until all racecars are done?
    End Sub

    Sub CreateRace()
        For index As Integer = 1 to 5
            raceTrack.Add(new RaceCar(index))
        Next
    End Sub

    Public Class RaceCar
        Private testThread as Thread =  New Thread(AddressOf StartEngine)
        Private carId as Integer
        Public Sub New(ByVal carNumber as Integer)
            me.carId = carNumber
            Me.TestThread.Start()
            Me.TestThread.Join()
        End Sub

        Public Sub StartEngine()
            Console.WriteLine(String.Format("Car#{0} is going!", Me.carId))
            Thread.Sleep(100*Me.carId)
            Console.WriteLine(String.Format("Car#{0} is DONE!", Me.carId))
        End Sub

    End Class

End Module
Community
  • 1
  • 1
Ken
  • 641
  • 3
  • 11
  • 25
  • You could increment a `counter` in your calling program for each class you instantiate and have each class raise an `Event` when its thread terminates. Then in your calling program make an `EventHandler` that decrements the `counter`. When `counter == 0` all threads have terminated. –  Feb 14 '12 at 20:41
  • @Michael That sounds pretty good, I will give that a try. – Ken Feb 14 '12 at 20:48
  • Tudor's answer has a good point about a flaw in your code. –  Feb 14 '12 at 20:50

2 Answers2

1

Apparently your RaceCar constructor only returns when the spawned thread finishes, since you are calling Join.

    Public Sub New(ByVal carNumber as Integer)
        me.carId = carNumber
        Me.TestThread.Start()
        Me.TestThread.Join()
    End Sub

I don't know if this is intentional (most likely not, because you are creating your RaceCar instances in a loop, so you have no parallelism, since the next instance cannot be created until the previous constructor call returns), but definitely your main thread cannot finish until all the child threads have finished.

I suggest the following restructuring:

Remove the Join from the constructor and add a new method in your RaceCar class:

Public Sub JoinThread()
    Me.TestThread.Join()
End Sub

Now the object creation loop will not block. Add another loop afterwards:

Sub CreateRace()
    For index As Integer = 1 to 5
        raceTrack.Add(new RaceCar(index))
    Next

    // other work maybe?

    For Each car As RaceCar In raceTrack
        car.JoinThread()
    Next
End Sub

Now your threads will start in the first loop, run their work in parallel and eventually stop on the new "barrier loop".

Note that the main thread can still do other tasks between the two loops.

(Disclaimer: I've never programmed in vb.net, so hopefully what I wrote is not totally wrong.)

Tudor
  • 61,523
  • 12
  • 102
  • 142
  • Does that mean when the StartEngine() is done, the thread will terminate automatically?? I did the Join() right after because 1) other examples I've seen (ex. research link) had a similar thing where the threads were created then immediately joined again. and 2) I thought I had to terminate the thread – Ken Feb 14 '12 at 20:53
  • 1
    Yes, exactly, a thread terminates when the method that it's supposed to execute returns. – Tudor Feb 14 '12 at 20:55
0

I'm not sure if I'm missing something (I bet this is a sample project) but I cannot reproduce the problem. I can see all the cars coming in when I run the code you posted (minus the .Join call as mentioned by Tudor).

But, if I set Thread.IsBackground to True, then I see your problem. In order to make sure your Threads are finishing before the Process terminates, do this:

Public Sub New(ByVal carNumber As Integer)
    Me.carId = carNumber
    Me.testThread.IsBackground = False ' <--This will cause threads to be 'Foreground'
    Me.testThread.Start()
End Sub

Take a look at the definition of Thread.IsBackground: http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.95).aspx

This property tells you whether or not the Process will wait for the Thread to finish before terminating. Obviously in your case, you want this to be False. It defaults to False in many cases, but there may be cases in which it's being set to true.

Nathan
  • 1,675
  • 17
  • 25