开发者

Multi-Threading - waiting for all threads to be signalled

I have sce开发者_开发百科narios where I need a main thread to wait until every one of a set of possible more than 64 threads have completed their work, and for that I wrote the following helper utility, (to avoid the 64 waithandle limit on WaitHandle.WaitAll())

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

With this utility method, however, each waithandle is only examined after every preceding one in the array has been signalled... so it is in effect synchronous, and will not work if the waithandles are autoResetEvent wait handles (which clear as soon as a waiting thread has been released)

To fix this issue I am considering changing this code to the following, but would like others to check and see if it looks like it will work, or if anyone sees any issues with it, or can suggest a better way ...

Thanks in advance:

    public static void WaitAllParallel(WaitHandle[] handles)
    {
        if (handles == null)
            throw new ArgumentNullException("handles",
                "WaitHandle[] handles was null");
        int actThreadCount = handles.Length;
        object locker = new object();
        foreach (WaitHandle wh in handles)
        {
            WaitHandle qwH = wh;
            ThreadPool.QueueUserWorkItem(
                 delegate
                 {
                     try { qwH.WaitOne(); }
                     finally { lock(locker) --actThreadCount; }
                 });
        }
        while (actThreadCount > 0) Thread.Sleep(80);
    }


If you know how many threads you have, you can use an interlocked decrement. This is how I usually do it:

 {
 eventDone = new AutoResetEvent();
 totalCount = 128;
 for(0...128) {ThreadPool.QueueUserWorkItem(ThreadWorker, ...);}
 }

 void ThreadWorker(object state)
 try
 {
   ... work and more work
 }
 finally
 {
   int runningCount = Interlocked.Decrement(ref totalCount);
   if (0 == runningCount)
   {
     // This is the last thread, notify the waiters
     eventDone.Set();
   }
 }

Actually, most times I don't even signal but instead invoke a callback continues the processing from where the waiter would continue. Less blocked threads, more scalability.

I know is different and may not apply to your case (eg. for sure will not work if some of thoe handles are not threads, but I/O or events), but it may worth thinking about this.


I'm not sure what exactly you're trying to do, but would a CountdownEvent (.NET 4.0) conceptually solve your problem?


I'm not a C# or .NET programmer, but you could use a semaphore that is posted when one of your worker threads exits. The monitoring thread would simply wait on the semaphore n times where n is the number of worker threads. Semaphores are traditionally used to count resources in use but they can be used to count jobs completed by waiting on the same semaphore for n times.


When working with lots of simultaneous threads, I prefer to add each thread's ManagedThreadId into a Dictionary when I start the thread, and then have each thread invoke a callback routine that removes the dying thread's id from the Dictionary. The Dictionary's Count property tells you how many threads are active. Use the value side of the key/value pair to hold info that your UI thread can use to report status. Wrap the Dictionary with a lock to keep things safe.


ThreadPool.QueueUserWorkItem(o =>
{
    try
    {
        using (var h = (o as WaitHandle))
        { 
            if (!h.WaitOne(100000))
            {
                // Alert main thread of the timeout
            }
        }
    }
    finally
    {
        Interlocked.Decrement(ref actThreadCount);
    }
 }, wh);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜