views:

444

answers:

9

Hello,

I have an application with one main thread and N worker threads. At some point I need that the main thread waits until all the N threads have completed one section of their work.

I normally would use Monitor.Wait() and Monitor.Pulse() but this will prevent the N threads from working at the same time.

Any idea on how to do that?

Thanks in advance.

A: 

It's called a barrier: http://programmingexamples.wikidot.com/java-barrier

Ow, but if you only need the first thread to wait for the rest to pass some point and you want the other to still keep working, then use a semaphore of size N and let all other threads take it, while that first thread waits to acquire it after them..

Semaphore: http://programmingexamples.wikidot.com/java-semaphore

rmn
+1  A: 

If you just need to wait until the threads terminate, how about Thread.Join? In .NET 4.0 you could use Task.WaitAll. If you need to wait until they finish just part of their task it's a little tricker. In current versions of .NET look at WaitHandle.WaitAll/Threading.ManualResetEvent. In .NET 4.0 you can use Threading.Barrier.

Jason
Nope, the threads don't terminate, just go over a point in their code.
SoMoS
@SoMoS: Look at the examples on MSDN for `WaitHandle.WaitAll` and `ManualResetEvent`.
Jason
+2  A: 

Do something similar to garbage collection. You'll write a ThreadManager that has a count of how many threads are running. When the main thread starts a new worker, the ThreadManager will increment its count of workers. When a worker finishes, it will inform the ThreadManager who will decrement its count of threads. When it has zero worker threads, the ThreadManager will wake the main thread.

David Souther
don't forget to lock the count variable when modifying or reading it.
San Jacinto
+5  A: 

.NET 4.0 will include the System.Threading.Barrier class that will make synchronization between multiple threads easier. A blog post with some good example code can be found here.

Similar functionality can be achieved using multiple WaitHandles in .NET 3.0+, as demonstrated in this example on MSDN.

A brief summary of the MSDN example:

const int numberOfWorkers = 5;

static void Main()
{
    var handles = new ManualResetEvent[numberOfWorkers];

    for (int i = 0; i < numberOfWorkers; i++)
    {
        handles[i] = new ManualResetEvent(false);
        ThreadPool.QueueUserWorkItem(o => worker.Work(), null);
    }

    // Wait for all workers to finish before continuing
    WaitHandle.WaitAll(handles);
    /* continue execution... */
}
Justin R.
Looks like what I need, but I need this now in 3.5. There is a way of doing this?
SoMoS
@SoMoS, if I understand what you're going for, you can do what you want in 3.5 using `WaitAll` with `AutoResetEvents` (which I describe in my answer).
Jeff Sternal
@Jeff Sternal: I believe we're both proposing the same solution, but with different implementations.
Justin R.
@Justin - agreed, and with the latest update (in the question's comments), it seems like a `ManualResetEvent` is more appropriate.
Jeff Sternal
+1  A: 

Since on some implementations, there is a limit to how many handles WaitHandle.WaitAll() can .... handle, (see msdn-WaitHandle.WaitAll(), I have created a utility method for this:

    public static void WaitAll(WaitHandle[] handles)
    {
        if (handles == null)
            throw new ArgumentNullException("handles",
                "WaitHandle[] handles was null");
        foreach (WaitHandle wh in handles) wh.WaitOne();
    }

usage is to add the wait handle for each thread to an array, and then call the above utility method (passing the array) after All threads have been initiated.

 List<WaitHandle> waitHndls = new List<WaitHandle>();
 foreach (MyType mTyp in MyTypeCollection)
 {
     ManualResetEvent txEvnt = new ManualResetEvent(false);
     int qryNo1 = ++qryNo;
     ThreadPool.QueueUserWorkItem(
        delegate
           {
              try
              {
                  // Code to execute whatever thread's function is... 
              }
              catch (SomeCustomException iX)
              {
                  // catch code
              }                                         }
              finally {  lock (locker) txEvnt.Set();  }
           });
     waitHndls.Add(txEvnt);
 }
 util.WaitAll(waitHndls.ToArray());
Charles Bretana
Why not use `WaitHandle.WaitAll`?
Jeff Sternal
Because there is a limit on how many handles WaitAll can .... no pun intended) ... handle... From msdn, "The WaitAll method returns when all the handles are signaled. On some implementations, if more than 64 handles are passed, a NotSupportedException is thrown."
Charles Bretana
True, but the `foreach` approach doesn't work with `AutoResetEvents` (or more precisely, it will produce unpredictable results, and can cause deadlocks).
Jeff Sternal
Maybe I will have to implement this approach if I reach the 64 threads limitation. Thanks for your contribution.
SoMoS
@Jeff Sternal, If I understand autoresetEvents properly, then you are correct. This would be totally inappropriate for autoResetEvents. But then the entire concept of "waiting" for every event in a collection of autoresetEvents itself doesn't make any sense... How can you wait for every event to be set, when every time you set one it gets reset (that is, cleared, ... assuming at least one thread is waiting)?
Charles Bretana
@Jeff Sternal, In this case, my utility IS waiting for them all to be clear... And if were waiting for them all to have been signalled at least once, how would you do that with WaitAll()? My understanding is that with say 10 AutoResetEvents if you call WaitAll() it will block until all 10 are concurrently in a signalled state. Are you saying that it will only block until each of the ten has signaled at least once since the WaitAll() call? It certainly cannot know about signals which occurred vefore you called WaitAll()!
Charles Bretana
@Charles - no, you're right, I overstated the problem in my initial comment; sequentially waiting on the handles is really only a problem in a few circumstances, primarily when you want to `Wait` with a timeout (though that's often a good idea!) There's a good discussion of it here: http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-clr/1971/WaitHandles-must-be-less-than-or-equal-to-64-missing-documentation. (Addressing the earlier comment, when you `WaitAll` on `AutoResetEvents`, no single handle is cleared until the waiting thread releases them all.)
Jeff Sternal
@Jeff ,in researching to understand yr comment, however, I notice that my utility only starts to wait on each waithandle until all previous waithandles in the passed in array have been signalled. So I wonder if it might be better to start waiting on each one immediately (creating another whole set of queued threads in the utility) and letting the utility itself wait on all of them in parallel... That way it WOULD handle autoResetEvents as well. What do you think? –
Charles Bretana
+2  A: 

It seems like WaitHandle.WaitAll should solve this problem.

Your main thread will need to keep references to worker thread wait handles. When it needs to synchronize, pass those handles into the above method. Worker threads signal at the appropriate point in their code.

If worker threads loop or need to 'pulse' multiple times, you could use AutoResetEvents, like this:

public void WorkerMethod() {         
     DoFirstThing();
     this.autoResetEvent.Set();
     DoSecondThing();
     this.autoResetEvent.Set();
     // etc.
}

If not (if the main thread just needed to know the worker thread had passed some threshold), ManualResetEvents would be fine.

There are a few things to be wary of when using WaitAll (from the MSDN WaitAll documentation):

On some implementations, if more than 64 handles are passed, a NotSupportedException is thrown. If the array contains duplicates, the call fails with a DuplicateWaitObjectException.

However, it's rare that a process can really take advantage of more than 64 threads, so this limitation often won't be important.

Jeff Sternal
I liked your approach. I have to take into account that this is for processing incoming tcp clients so maybe I have more that 64 threads (one for each client). How can I check where this limitation applies?
SoMoS
Unfortunately, that information is hard to come by. I can see in Reflector that it was hard-coded into the .NET 2.0 implementation, but don't have the 3.5 assemblies at hand. Here's an article that proposes an ingenious solution to overcoming the limit, which is similar to what David Souther suggests in another answer: create your own `WaitHandle` class that implements reference counting so that you share a single `WaitHandle` across all your worker threads. http://msdn.microsoft.com/en-us/magazine/cc163914.aspx.
Jeff Sternal
A: 

Use Thread.Join (that blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping) method like in the example:

using System;
using System.Threading;

class IsThreadPool
{
    static void Main()
    {
        AutoResetEvent autoEvent = new AutoResetEvent(false);

        Thread regularThread = 
            new Thread(new ThreadStart(ThreadMethod));
        regularThread.Start();
        ThreadPool.QueueUserWorkItem(new WaitCallback(WorkMethod), 
            autoEvent);

        // __________ Wait for foreground thread to end. __________ 
        regularThread.Join();

        // Wait for background thread to end.
        autoEvent.WaitOne();
    }

    static void ThreadMethod()
    {
        Console.WriteLine("ThreadOne, executing ThreadMethod, " +
            "is {0}from the thread pool.", 
            Thread.CurrentThread.IsThreadPoolThread ? "" : "not ");
    }

    static void WorkMethod(object stateInfo)
    {
        Console.WriteLine("ThreadTwo, executing WorkMethod, " +
            "is {0}from the thread pool.", 
            Thread.CurrentThread.IsThreadPoolThread ? "" : "not ");

        // Signal that this thread is finished.
        ((AutoResetEvent)stateInfo).Set();
    }
}
serhio
I'm not waiting for other threads to terminate, just to go over a certain point.
SoMoS
@SoMoS: I understand. Why not divide your task by two threads then? :) just wait the first, and leave the second.
serhio
Does not look very good to split the work in two threads and the first one starting the second one when it ends. What if I need to check 2 points instead of 1? Thanks anyway mate!
SoMoS
@SoMoS: I think you can't have 2 points because the main thread can wait only once your thread(after starting it), after that it let it go...
serhio
+1  A: 

Ok, what I'm doing now (using your ideas) and seems to work is this:

I declared a list of ManualResetEvent:

Private m_waitHandles As List(Of Threading.ManualResetEvent)

The process accepts incoming Tcp connections and starts one thread on each connection. So at the new client handler I've added this code:

Dim waitHandle As Threading.ManualResetEvent
waitHandle = New Threading.ManualResetEvent(True)
SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
    m_waitHandles.Add(waitHandle)
End SyncLock

''# Do all the work
StoppableMethod()

SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
    waitHandle = m_waitHandles.Item(Threading.WaitHandle.WaitAny(m_waitHandles.ToArray()))
End SyncLock

waitHandle.Reset()
NonStoppableMethod()
waitHandle.Set()

SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
    m_waitHandles.Remove(waitHandle)
End SyncLock

The last thing done is to modify the Stop method to be sure that the Stop operation will not done with any thread inside the NonStoppableMethod:

SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
    If m_waitHandles.Count > 0 Then
        Threading.WaitHandle.WaitAll(m_waitHandles.ToArray())
    End If
End SyncLock

I'm not sure that this is done in a right way because it's the first time I deal with things like that. Do you feel that this is ok and is a good approach?

Thanks to all, mates!

SoMoS
Glad we could help!
Justin R.
A: 

Try using this:

int threadsCompleted = 0;
int numberOfThreads = 4;
ManualResetEvent completedEvent = new ManualResetEvent(false);

In each thread:

// Do task

if (Interlocked.Increment(threadsCompleted) == numberOfThreads)
    completedEvent.Set();

Main thread:

completedEvent.WaitOne();
wj32