Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / threads

Handler for WaitAll() and WaitAny() limitation problem

5.00/5 (4 votes)
12 Jul 2013CPOL1 min read 25.2K   90  
Handler for WaitAll() and WaitAny() limitation problem.

Introduction

WaitHandles must be less than or equal to 64? This exception is thrown when a developer wants to do WaitAll() or WaitAny() for the field resetEvents (which is WaitHandled[] resetEvents) where the capacity is greater than 64. If you look at the source code of the WaitHandled class in the .NET Framework you will see that there is a condition if("some field"<64). I built ThreadWaitHandle.cs which includes static methods WaitAll() and WaitAny() for solving this problem manually.

Using the code

This class includes two static methods WaitAll() and WaitAny(). Like WaitHandle developer sends an array to this method and gets a result. For solving limitation problems I use recursion and reset events for invoking all subarrays at the same time. Simply copy ThreadWaitHandler.cs and paste it in your project for use.

C#
public class ThreadWaitHandler
{
    private const int Limit = 64; //Maximal limit for original WaitAll() and WaitAny()

    public static void WaitAll(WaitHandle[] waitHandles)
    {
        WaitAllHandled(waitHandles);
    }
 
    public static void WaitAllHandled(WaitHandle[] waitHandles)
    {
        if (waitHandles.Length <= Limit)
        /*Check if array.lenght less or equal to 64 then WaitAll()*/
        {
            WaitHandle.WaitAll(waitHandles);
            return;
        }

        /*this event will wait threads for getting correct result.Same time Invokes WaitAll()*/
        ManualResetEvent resetEvent = new ManualResetEvent(false);

        for (int i = 0; i < waitHandles.Length / Limit + 1; i++)
        {
            int localI = i;

            new Thread(() =>
            { 
                /* each thread will divide array to subarrays and check*/
                WaitAllHandled(waitHandles.Skip(localI * Limit).Take(
                  GetNumberOfTakedElements(localI, waitHandles)).ToArray());

                resetEvent.Set();

            }).Start();
        }

        resetEvent.WaitOne();
    }

    /*---------------------------------------WaitAny()------------------------------*/

    public static int WaitAny(WaitHandle[] waitHandles)
    {
        return waitAnyHandled(waitHandles, 0);
    }
 
    private static int waitAnyHandled(WaitHandle[] waitHandles, int offset)
    {
        if (waitHandles.Length <= Limit)  /* also there recursion check condition*/
        {
            int result = WaitHandle.WaitAny(waitHandles) + offset * Limit;

            return result;
        }

        ManualResetEvent[] resetEvents = 
          new ManualResetEvent[(int)Math.Ceiling(waitHandles.Length / (double)Limit)];
        DataRecursion[] dataRecursions = new DataRecursion[resetEvents.Length];

        for (int i = 0; i < resetEvents.Length; i++)
        {
            ManualResetEvent resetEvent = new ManualResetEvent(false);
            resetEvents[i] = resetEvent;
            int localI = i;
 
            new Thread(() =>
            {
                /* each thread will divide array to subarrays and check. 
                   Also method will give ManualResetEvent reference for each thread*/
                int originalIndex = waitAnyHandled(waitHandles.Skip(localI * Limit).Take(
                  GetNumberOfTakedElements(localI, waitHandles)).ToArray(), localI);

                dataRecursions[localI] = new DataRecursion(originalIndex, localI);

                resetEvent.Set();

            }).Start();
        }

        int index = WaitHandle.WaitAny(resetEvents);

        return GetWaitAnyResultByIndex(index, dataRecursions);
    }

    private static int GetWaitAnyResultByIndex(int index, DataRecursion[] dataRecursions)
    {
        for (int i = 0; i < dataRecursions.Length; i++)
        {
            if (i == index)
            {
                return dataRecursions[i].Originalindex;
            }
        }

        return -1;
    }

    private static int GetNumberOfTakedElements(int skip, WaitHandle[] waitHandles)
    {
        if (((int)Math.Ceiling(waitHandles.Length / (double)Limit) - skip) == 1)
        {
            return waitHandles.Length - skip * Limit;
        }

        return Limit;
    }
}

/*This class using as memory in recursion such that each step 
  of recursion sets data to "dataRecursion" reference*/

public class DataRecursion
{
    public int Originalindex { get; set; }
    public int EventIndex { get; set; }

    public DataRecursion(int orginalIndex, int eventIndex)
    {
        Originalindex = orginalIndex;
        EventIndex = eventIndex;
    }
}

Points of Interest

I think this way we will get an optimal solution  for this problem. I have read other solutions but for WaitAny() the solutions were unacceptable. If you want to use 500 or more threads, you must pay attention to speed of compilation. As when you compile the application the first thread will be set first until others are reading.

History  

I will submit all updates and changes here. If you have another solution, please write and discuss.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)