Workaround for the WaitHandle.WaitAll 64 handle limit?

前端 未结 9 1880
孤独总比滥情好
孤独总比滥情好 2020-11-28 04:19

My application spawns loads of different small worker threads via ThreadPool.QueueUserWorkItem which I keep track of via multiple ManualResetEvent

相关标签:
9条回答
  • Windows XP SP3 supports maximum two WaitHandles. For cases more than 2 WaitHandles Application prematurely terminates.

    0 讨论(0)
  • 2020-11-28 05:08

    Here is another solution. Here is the "events" is a list of ManualResetEvent. The size of the list can be greater than 64 (MAX_EVENTS_NO).

    int len = events.Count;
    if (len <= MAX_EVENTS_NO)
        {
            WaitHandle.WaitAll(events.ToArray());
        } else {
            int start = 0;
            int num = MAX_EVENTS_NO;
            while (true)
                {
                    if(start + num > len)
                    {
                       num = len - start;
                    }
                    List<ManualResetEvent> sublist = events.GetRange(start, num);
                    WaitHandle.WaitAll(sublist.ToArray());
                    start += num;
                    if (start >= len)
                       break;
               }
       }
    
    
    0 讨论(0)
  • 2020-11-28 05:13

    Your workaround is not correct. The reason is that the Set and WaitOne could race if the last work item causes the threadCount to go to zero before the queueing thread has had to chance to queue all work items. The fix is simple. Treat your queueing thread as if it were a work item itself. Initialize threadCount to 1 and do a decrement and signal when the queueing is complete.

    int threadCount = 1;
    ManualResetEvent finished = new ManualResetEvent(false);
    ...
    Interlocked.Increment(ref threadCount); 
    ThreadPool.QueueUserWorkItem(delegate 
    { 
        try 
        { 
             // do work 
        } 
        finally 
        { 
            if (Interlocked.Decrement(ref threadCount) == 0) 
            { 
                 finished.Set(); 
            } 
        } 
    }); 
    ... 
    if (Interlocked.Decrement(ref threadCount) == 0)
    {
      finished.Set();
    }
    finished.WaitOne(); 
    

    As a personal preference I like using the CountdownEvent class to do the counting for me.

    var finished = new CountdownEvent(1);
    ...
    finished.AddCount();
    ThreadPool.QueueUserWorkItem(delegate 
    { 
        try 
        { 
             // do work 
        } 
        finally 
        { 
          finished.Signal();
        } 
    }); 
    ... 
    finished.Signal();
    finished.Wait(); 
    
    0 讨论(0)
提交回复
热议问题