How do I wait until all threads have completed

T

Thirsty Traveler

I would like to create a test harness that simulates multiple concurrent
users executing an individual thread. I would like this to be determined at
runtime when the user specifies the number of desired threads. When this is
kicked off, I would like to wait in the primary thread until all worker
threads have completed and time the result... one problem... I can't figure
out how to wait for all threads to complete prior to updating my timing.

Does anyone know how this could be done?
 
C

Carl Daniel [VC++ MVP]

Thirsty said:
I would like to create a test harness that simulates multiple
concurrent users executing an individual thread. I would like this to
be determined at runtime when the user specifies the number of
desired threads. When this is kicked off, I would like to wait in the
primary thread until all worker threads have completed and time the
result... one problem... I can't figure out how to wait for all
threads to complete prior to updating my timing.
Does anyone know how this could be done?

1. Accumulate thread handles for all of the threads that you create.
2. After you've created all of the threads, wait on each of the thread
handles one by one in any order.

When the last wait is completed, all of your threads have terminated.

-cd
 
T

Thirsty Traveler

How do I wait? Do you have a code snippet?

Carl Daniel said:
1. Accumulate thread handles for all of the threads that you create.
2. After you've created all of the threads, wait on each of the thread
handles one by one in any order.

When the last wait is completed, all of your threads have terminated.

-cd
 
T

Tom Spink

Thirsty said:
How do I wait? Do you have a code snippet?

Hi,

Take a look at the WaitHandle class, in System.Threading. It contains a
static method called WaitAll, and this will block the calling thread until
all the WaitHandle's passed in as the parameter are signalled.

What you'll need to do is create an AutoResetEvent for each thread, and when
a thread completes, call the 'Set' method on the AutoResetEvent.
AutoResetEvent inherits from WaitHandle, so all you do is pass an array of
the AutoResetEvent's into the WaitAll method, and when each and every
AutoResetEvent is signalled (i.e. Set has been called) the thread calling
WaitAll will resume.

Hope this helps,
-- Tom Spink
 
C

Carl Daniel [VC++ MVP]

Tom said:
What you'll need to do is create an AutoResetEvent for each thread,
and when a thread completes, call the 'Set' method on the
AutoResetEvent. AutoResetEvent inherits from WaitHandle, so all you
do is pass an array of the AutoResetEvent's into the WaitAll method,
and when each and every AutoResetEvent is signalled (i.e. Set has
been called) the thread calling WaitAll will resume.

No, you don't need to do that - you can wait directly on the Thread itself.
For the .NET Thread object, waiting for the thread to complete is done by
calling the Join member function. Internally that'll end up calling
WaitForSingleObject on the thread handle itself, which becomes signalled
when the thread terminates.

-cd
 
T

Tom Spink

Carl said:
No, you don't need to do that - you can wait directly on the Thread
itself. For the .NET Thread object, waiting for the thread to complete is
done by
calling the Join member function. Internally that'll end up calling
WaitForSingleObject on the thread handle itself, which becomes signalled
when the thread terminates.

-cd

This is true, but then you'd need to call 'Join' sequentially on each
running worker thread.

-- Tom Spink
 
C

Carl Daniel [VC++ MVP]

Tom said:
This is true, but then you'd need to call 'Join' sequentially on each
running worker thread.

Yep - which accomplishes the sames thing as WaitAll on a bunch of Event
handles much more efficiently, with fewer opportunities for error and with
fewer resources (since you don't need events).

-cd
 
T

Thirsty Traveler

Carl Daniel said:
Yep - which accomplishes the sames thing as WaitAll on a bunch of Event
handles much more efficiently, with fewer opportunities for error and with
fewer resources (since you don't need events).

-cd

I am getting close... I did get the join to work but this does not seem to
be the best way to go. In addition, I am still unclear as to how to start an
aribitrary number of threads.

I am trying to do something like:

private void btnSubmit_Click(object sender, EventArgs e)
{
int iNoThreads = Convert.ToInt16(tbNoThreads.Text)
for (int i=0; i<iNoThreads; i++)
{
// create the test process thread
}
// wait for all test "SubmitOrders" process threads to complete
// display elapsed time and other metrics
}

private void SubmitOrders()
{
// random order submits
}

So far, I have gotten the code below to work, but it does not allow for the
creation of an arbitrary number of threads.

private void btnSubmit_Click(object sender, EventArgs e)
{
int iNoTxns = Convert.ToInt16(tbNoTxns.Text);
tbElapsedTime.Text = null;
tbElapsedTime.Refresh();
DateTime startTime = DateTime.Now;

ThreadStart entrypoint = new ThreadStart(SubmitOrders);
Thread thread = new Thread(entrypoint);
thread.Name = "SubmitOrders01";
thread.Start();
thread.Join();

DateTime stopTime = DateTime.Now;
TimeSpan duration = stopTime - startTime;
tbElapsedTime.Text = duration.Hours + ":" + duration.Minutes + ":" +
duration.Seconds + ":" + duration.Milliseconds;
}
 
T

Tom Spink

Thirsty said:
Carl Daniel said:
Tom said:
Carl Daniel [VC++ MVP] wrote:

Tom Spink wrote:
What you'll need to do is create an AutoResetEvent for each thread,
and when a thread completes, call the 'Set' method on the
AutoResetEvent. AutoResetEvent inherits from WaitHandle, so all you
do is pass an array of the AutoResetEvent's into the WaitAll method,
and when each and every AutoResetEvent is signalled (i.e. Set has
been called) the thread calling WaitAll will resume.

No, you don't need to do that - you can wait directly on the Thread
itself. For the .NET Thread object, waiting for the thread to
complete is done by
calling the Join member function. Internally that'll end up calling
WaitForSingleObject on the thread handle itself, which becomes
signalled when the thread terminates.

-cd

This is true, but then you'd need to call 'Join' sequentially on each
running worker thread.

Yep - which accomplishes the sames thing as WaitAll on a bunch of Event
handles much more efficiently, with fewer opportunities for error and
with fewer resources (since you don't need events).

-cd

I am getting close... I did get the join to work but this does not seem to
be the best way to go. In addition, I am still unclear as to how to start
an aribitrary number of threads.

I am trying to do something like:

private void btnSubmit_Click(object sender, EventArgs e)
{
int iNoThreads = Convert.ToInt16(tbNoThreads.Text)
for (int i=0; i<iNoThreads; i++)
{
// create the test process thread
}
// wait for all test "SubmitOrders" process threads to complete
// display elapsed time and other metrics
}

private void SubmitOrders()
{
// random order submits
}

So far, I have gotten the code below to work, but it does not allow for
the creation of an arbitrary number of threads.

private void btnSubmit_Click(object sender, EventArgs e)
{
int iNoTxns = Convert.ToInt16(tbNoTxns.Text);
tbElapsedTime.Text = null;
tbElapsedTime.Refresh();
DateTime startTime = DateTime.Now;

ThreadStart entrypoint = new ThreadStart(SubmitOrders);
Thread thread = new Thread(entrypoint);
thread.Name = "SubmitOrders01";
thread.Start();
thread.Join();

DateTime stopTime = DateTime.Now;
TimeSpan duration = stopTime - startTime;
tbElapsedTime.Text = duration.Hours + ":" + duration.Minutes + ":" +
duration.Seconds + ":" + duration.Milliseconds;
}

Hi Thirsty,

Perhaps this is what you're trying to do:

(I also optimised your code slightly <g>)

///
private void btnSubmit_Click ( object sender, EventArgs e )
{
int iNoTxns = int.Parse( tbNoTxns.Text );
tbElapsedTime.Text = null;
tbElapsedTime.Refresh();
DateTime startTime = DateTime.Now;

// Start the threads.
List<Thread> threads = new List<Thread>();
for ( int i = 0; i < iNoTxns; i++ )
{
Thread thread = new Thread( SubmitOrders );
thread.Name = string.Format( "SubmitOrders{0}", i );
thread.Start();
}

// Wait for the threads.
foreach ( Thread thread in threads )
thread.Join();

threads.Clear();

DateTime stopTime = DateTime.Now;
TimeSpan duration = stopTime - startTime;
tbElapsedTime.Text = string.Format( "{0}:{1}:{2}:{3}", duration.Hours,
duration.Minutes, duration.Seconds, duration.Milliseconds );
}
///

I've used the Thread.Join() method here to wait for thread completion, but
if it was me, I would use the WaitHandles and the WaitHandle.WaitAll()
method I suggested in my other post, which is specifically designed for
waiting for multiple objects, as opposed to this, which sequentially waits
for each thread to complete.

What do other people think?

Hope this helps,
-- Tom Spink
 
C

Carl Daniel [VC++ MVP]

Tom said:
I've used the Thread.Join() method here to wait for thread
completion, but if it was me, I would use the WaitHandles and the
WaitHandle.WaitAll() method I suggested in my other post, which is
specifically designed for waiting for multiple objects, as opposed to
this, which sequentially waits for each thread to complete.

What do other people think?

Joining each thread individually has the advantage of supporting any number
of threads.

Using WaitAll, you can only wait on 64 handles at once. The documentation
for WaitHandle.WaitAll says that on some platforms if you wait on more than
64 handles it will fail. As far as I know, "some platforms" could be
re-written "all current platforms" and it would be equally true.

-cd
 
J

Jon Skeet [C# MVP]

Tom Spink wrote:

I've used the Thread.Join() method here to wait for thread completion, but
if it was me, I would use the WaitHandles and the WaitHandle.WaitAll()
method I suggested in my other post, which is specifically designed for
waiting for multiple objects, as opposed to this, which sequentially waits
for each thread to complete.

What do other people think?

I would personally use Thread.Join - it's clearer to me what that's
doing, as you don't need any extra concepts (WaitHandles).

Jon
 
T

Thirsty Traveler

Tom Spink said:
Hi Thirsty,

Perhaps this is what you're trying to do:

(I also optimised your code slightly <g>)

///
private void btnSubmit_Click ( object sender, EventArgs e )
{
int iNoTxns = int.Parse( tbNoTxns.Text );
tbElapsedTime.Text = null;
tbElapsedTime.Refresh();
DateTime startTime = DateTime.Now;

// Start the threads.
List<Thread> threads = new List<Thread>();
for ( int i = 0; i < iNoTxns; i++ )
{
Thread thread = new Thread( SubmitOrders );
thread.Name = string.Format( "SubmitOrders{0}", i );
thread.Start();
}

// Wait for the threads.
foreach ( Thread thread in threads )
thread.Join();

threads.Clear();

DateTime stopTime = DateTime.Now;
TimeSpan duration = stopTime - startTime;
tbElapsedTime.Text = string.Format( "{0}:{1}:{2}:{3}", duration.Hours,
duration.Minutes, duration.Seconds, duration.Milliseconds );
}
///

I've used the Thread.Join() method here to wait for thread completion, but
if it was me, I would use the WaitHandles and the WaitHandle.WaitAll()
method I suggested in my other post, which is specifically designed for
waiting for multiple objects, as opposed to this, which sequentially waits
for each thread to complete.

What do other people think?

Hope this helps,
-- Tom Spink

This is elegant and I like it... especially in that I didn't even consider
using a generic to solve my problem of creating arbitrary numbers of
threads..

However, the Join does not appear to be working. My test program places
orders on a mainframe, which results in an elapsed time of approximately
500ms or so. Using the above code, my timer is showing an elapsed time of
approximately 100ms, which leads me to believe it is not blocking.
 
T

Thirsty Traveler

I just put a Thread.Sleep(3000) in the worker thread and am still getting
the 100ms elapsed time, so the Join clearly does not appear to be working.
 
T

Tom Spink

Carl said:
Joining each thread individually has the advantage of supporting any
number of threads.

Using WaitAll, you can only wait on 64 handles at once. The documentation
for WaitHandle.WaitAll says that on some platforms if you wait on more
than
64 handles it will fail. As far as I know, "some platforms" could be
re-written "all current platforms" and it would be equally true.

-cd

Hi Carl,
Using WaitAll, you can only wait on 64 handles at once.

Interesting, I did not know that.

-- Tom Spink
 
T

Tom Spink

Thirsty said:
I just put a Thread.Sleep(3000) in the worker thread and am still getting
the 100ms elapsed time, so the Join clearly does not appear to be working.

You know why that is... I'm a muppet and forgot to add the thread instance
to the list. O_o

Check this out:

///
for ( int i = 0; i < iNoTxns; i++ )
{
  Thread thread = new Thread( SubmitOrders );
  thread.Name = string.Format( "SubmitOrders{0}", i );
  thread.Start();
threads.Add( thread );
}
///

Notice the 'threads.Add( ... )' I put in there at the end of the for loop.

Whoops!
-- Tom Spink
 
B

Barry Kelly

Tom Spink said:
I've used the Thread.Join() method here to wait for thread completion, but
if it was me, I would use the WaitHandles and the WaitHandle.WaitAll()
method I suggested in my other post, which is specifically designed for
waiting for multiple objects, as opposed to this, which sequentially waits
for each thread to complete.

You still can't continue until all threads are finished, so it doesn't
matter whether you sequentially wait or use WaitAll(), and thus it
doesn't make sense introducing more synchronization objects that will
need to be cleaned up separately.

WaitAll() makes more sense when you need to acquire two or more
synchronization objects simultaneously, without ordering issues.

-- Barry
 
T

Tom Spink

Barry said:
You still can't continue until all threads are finished, so it doesn't
matter whether you sequentially wait or use WaitAll(), and thus it
doesn't make sense introducing more synchronization objects that will
need to be cleaned up separately.

WaitAll() makes more sense when you need to acquire two or more
synchronization objects simultaneously, without ordering issues.

-- Barry

Hi Barry,

Good point, I like it. :)

-- Tom Spink
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top