Workaround for the Waithandle.Waitall 64 Handle Limit

Workaround for the WaitHandle.WaitAll 64 handle limit?

Create a variable that keeps track of the number of running tasks:

int numberOfTasks = 100;

Create a signal:

ManualResetEvent signal = new ManualResetEvent(false);

Decrement the number of tasks whenever a task is finished:

if (Interlocked.Decrement(ref numberOftasks) == 0)
{

If there is no task remaining, set the signal:

    signal.Set();
}

Meanwhile, somewhere else, wait for the signal to be set:

signal.WaitOne();

Workaround for the WaitHandle.WaitAll 64 handle limit?

Create a variable that keeps track of the number of running tasks:

int numberOfTasks = 100;

Create a signal:

ManualResetEvent signal = new ManualResetEvent(false);

Decrement the number of tasks whenever a task is finished:

if (Interlocked.Decrement(ref numberOftasks) == 0)
{

If there is no task remaining, set the signal:

    signal.Set();
}

Meanwhile, somewhere else, wait for the signal to be set:

signal.WaitOne();

Handle more than 64 thread at the same time

The winapi restriction on the number of handles you can wait on at the same time is a pretty hard one. It is just not necessary, you'll get the exact same outcome if you wait for each individual one:

' Wait for all threads in pool to calculate.
For i As Integer = 0 To FibonacciCalculations
doneEvents(i).WaitOne()
Next

And note how you can now combine this with the next loop, making your program more efficient since you overlap the calculation with the display. So you really want to favor this instead:

' Display the results.
For i As Integer = 0 To FibonacciCalculations
doneEvents(i).WaitOne()
Dim f As Fibonacci = fibArray(i)
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN)
Next

How to implement waitAny on more than 64 handles?

You might want to consider implementing something like a queue for notification packets for the 'waitAny' thread to wait on. When one of your multitude of threads completes it's operation, it places a notification packet on the queue. Your waitAny turns into a wait on a single event that indicates something is in the queue.

C# Threads, WaitHandle.WaitAll

I would not use WaitHandle.WaitAll. There are a couple of problems with this approach.

  • There is a 64 handle limit.
  • It cannot be used on an STA thread.
  • It promotes patterns that depend on the creation of multiple WaitHandle instances which obviously consume resource.

Instead, I typically use the CountdownEvent class when I want to wait on multiple events. Now, the problem you will have with that is that it still requires you to call Wait on some thread which is exactly what you are trying to avoid. The standard mechanism to avoid making a blocking call is to use the ThreadPool.RegisterWaitForSingleObject method. But, unfortunately that takes a WaitHandle and CountdownEvent does not inherit from that class.

The solution is to create your own CountdownWaitHandle class that can be used in the ThreadPool.RegisterWaitForSingleObject method. This approach will allow you to specify a callback delegate that will be executed once the WaitHandle is signaled.

Here is the most basic implemenation for the CountdownWaitHandle class. You will have to add all of the necessary harding code yourself, but this will get you started.

public class CountdownWaitHandle : WaitHandle
{
private int m_Count = 0;
private ManualResetEvent m_Event = new ManualResetEvent(false);

public CountdownWaitHandle(int initialCount)
{
m_Count = initialCount;
}

public void AddCount()
{
Interlocked.Increment(ref m_Count);
}

public void Signal()
{
if (Interlocked.Decrement(ref m_Count) == 0)
{
m_Event.Set();
}
}

public override bool WaitOne()
{
return m_Event.WaitOne();
}
}

The idea here is that instead of using many different WaitHandle instances you use a single CountdownWaitHandle instance. Initialize the instance with the desired count and then call Signal to decrement the count. Once the count gets to zero the WaitHandle will go into the signaled state. So instead of calling Set on multiple WaitHandle instances and blocking with WaitHandle.WaitAll you now call Signal on this one instance and block by calling WaitOne. And again, you can push off the blocking call to the thread pool by using TheadPool.RegisterWaitForSingleObject which will invoke a callback when the WaitHandle is signaled.

Threading Exception: The number of WaitHandles must be less than or equal to 64

You don't need an array of 128 manual events to check for completion of all 128 threads.

Create only one manual reset event and a plain integer starting at 128. Decrement that integer using Interlocked.Decrement at the end of ProcessSingleCategoryProduct, and only signal the event when the count reaches zero:

if (Interlocked.Decrement(ByRef myCounter) = 0) myEvent.Set();

Then declare only one Threading.ManualResetEvent as opposed to an array of them, and you can call WaitOne instead of WaitAll on it, and you are done.

See also usr's comment for an easier alternative in case you have .NET 4.

Threadpool WaitAll 64 [Leadtools]

You waiting for those events does not cause the tasks to fail. More likely there is an exception which you do not notice (uncaught or swallowed).

Make your processing Task based and use Task.WaitAll. No limits there. You'll also get automatic error propagation.



Related Topics



Leave a reply



Submit