3

Hi all i have this exception when i run my app. i work on .net 3.5 so i cannot use Task

waitall for multiple handles on sta thread is not supported

this is the code :-

private void ThreadPopFunction(ContactList SelectedContactList, List<User> AllSelectedUsers)
{
        int NodeCount = 0;

        AllSelectedUsers.EachParallel(user =>
        {
            NodeCount++;
            if (user != null)
            {
                if (user.OCSEnable)
                {
                    string messageExciption = string.Empty;
                    if (!string.IsNullOrEmpty(user.SipURI))
                    {
                        //Lync.Lync.Lync lync = new Lync.Lync.Lync(AdObjects.Pools);
                        List<Pool> myPools = AdObjects.Pools;
                        if (new Lync.Lync.Lync(myPools).Populate(user, SelectedContactList, out messageExciption))
                        {
                        }
                    }
                }
            }
        });
}

and this is my extension method i use to work with multithreading

public static void EachParallel<T>(this IEnumerable<T> list, Action<T> action)
{
    // enumerate the list so it can't change during execution
    // TODO: why is this happening?
    list = list.ToArray();
    var count = list.Count();

    if (count == 0)
    {
        return;
    }
    else if (count == 1)
    {
        // if there's only one element, just execute it
        action(list.First());
    }
    else
    {
        // Launch each method in it's own thread
        const int MaxHandles = 64;
        for (var offset = 0; offset <= count/MaxHandles; offset++)
        {
            // break up the list into 64-item chunks because of a limitiation in WaitHandle
            var chunk = list.Skip(offset*MaxHandles).Take(MaxHandles);

            // Initialize the reset events to keep track of completed threads
            var resetEvents = new ManualResetEvent[chunk.Count()];

            // spawn a thread for each item in the chunk
            int i = 0;
            foreach (var item in chunk)
            {
                resetEvents[i] = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(new WaitCallback((object data) =>
                {
                    int methodIndex =
                        (int) ((object[]) data)[0];

                    // Execute the method and pass in the enumerated item
                    action((T) ((object[]) data)[1]);

                    // Tell the calling thread that we're done
                    resetEvents[methodIndex].Set();
                }), new object[] {i, item});
                i++;
            }

            // Wait for all threads to execute
            WaitHandle.WaitAll(resetEvents);
        }
    }
}

if you can help me, i'll appreciate your support

ebb
  • 9,297
  • 18
  • 72
  • 123
  • Umm, yeah. You're saying that you're in a "single-threaded" apartment. Only another thread can reset the event. If you can have only one thread, you have a deadlock. use a multi-thread apartment. – Peter Ritchie Apr 16 '13 at 17:06
  • @ken2k i cannot use `Task` because i use .net 3.5 , and the anther solution "http://stackoverflow.com/questions/4192834/waitall-for-multiple-handles-on-a-sta-thread-is-not-supported" solved by `Task` class, it just use by .net 4.0 or 4.5. – Eslam Elmoawen Apr 16 '13 at 17:13
  • @PeterRitchie Now you added this precision in the question, please see my answer. – ken2k Apr 16 '13 at 17:23

3 Answers3

2

OK, as you're using .Net 3.5, you can't use the TPL introduced with .Net 4.0.

STA thread or not, in your case there is a way more simple/efficient approach than WaitAll. You could simply have a counter and a unique WaitHandle. Here's some code (can't test it right now, but it should be fine):

// No MaxHandle limitation ;)
for (var offset = 0; offset <= count; offset++)
{
    // Initialize the reset event
    var resetEvent = new ManualResetEvent();

    // Queue action in thread pool for each item in the list
    int counter = count;
    foreach (var item in list)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback((object data) =>
                      {
                          int methodIndex =
                              (int) ((object[]) data)[0];

                          // Execute the method and pass in the enumerated item
                          action((T) ((object[]) data)[1]);

                          // Decrements counter atomically
                          Interlocked.Decrement(ref counter);

                          // If we're at 0, then last action was executed
                          if (Interlocked.Read(ref counter) == 0)
                          {
                              resetEvent.Set();
                          }
                      }), new object[] {i, item});
    }

    // Wait for the single WaitHandle
    // which is only set when the last action executed
    resetEvent.WaitOne();
}

Also FYI, ThreadPool.QueueUserWorkItem doesn't spawn a thread each time it's called (I'm saying that because of the comment "spawn a thread for each item in the chunk"). It uses a pool of thread, so it mostly reuses existing threads.

ken2k
  • 48,145
  • 10
  • 116
  • 176
0

For those like me, who need to use the examples. ken2k's solution is great and it works but with a few corrections (he said he didn't test it). Here is ken2k's working example (worked for me):

// No MaxHandle limitation ;)
for (var offset = 0; offset <= count; offset++)
{
    // Initialize the reset event
    var resetEvent = new ManualResetEvent(false);

    // Queue action in thread pool for each item in the list
    long counter = count;
    // use a thread for each item in the chunk
    int i = 0;
    foreach (var item in list)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback((object data) =>
                      {
                          int methodIndex =
                              (int) ((object[]) data)[0];

                          // Execute the method and pass in the enumerated item
                          action((T) ((object[]) data)[1]);

                          // Decrements counter atomically
                          Interlocked.Decrement(ref counter);

                          // If we're at 0, then last action was executed
                          if (Interlocked.Read(ref counter) == 0)
                          {
                              resetEvent.Set();
                          }
                      }), new object[] {i, item});
    }

    // Wait for the single WaitHandle
    // which is only set when the last action executed
    resetEvent.WaitOne();
}
harrygg
  • 664
  • 1
  • 7
  • 12
0

Actually there is a way to use (at least a good part) of the TPL in .net 3.5. There is a backport that was done for the Rx-Project.

You can find it here: http://www.nuget.org/packages/TaskParallelLibrary

Maybe this will help.

ruben_we
  • 21
  • 1
  • 3