49
  1. Why do I get this error message? "WaitAll for multiple handles on a STA thread is not supported."
  2. Should I use [MTAThreadAttribute] attribut? Update: Dosn't work with WPF applications!

Note: It error is at line WaitHandle.WaitAll(doneEvents); I'm using a standard WPF project.

private void Search()
{
    const int CPUs = 2;
    var doneEvents = new ManualResetEvent[CPUs];

    // Configure and launch threads using ThreadPool:
    for (int i = 0; i < CPUs; i++)
    {
        doneEvents[i] = new ManualResetEvent(false);
        var f = new Indexer(Paths[i], doneEvents[i]);
        ThreadPool.QueueUserWorkItem(f.WaitCallBack, i);
    }

    // Wait for all threads in pool 
    WaitHandle.WaitAll(doneEvents);
    Debug.WriteLine("Search completed!");
}

Update: The following solution doesn’t work for WPF applications! It is not possible to change the main application attribute to MTAThreadAttribute. It will result in the following error:

Error: "WaitAll for multiple handles on a STA thread is not supported."

casperOne
  • 73,706
  • 19
  • 184
  • 253
Amir Rezaei
  • 4,948
  • 6
  • 33
  • 49

5 Answers5

58

Actually I use the following to replace WaitHandle.WaitAll(doneEvents);

foreach (var e in doneEvents)
    e.WaitOne();
Alex
  • 3,429
  • 4
  • 36
  • 66
Calimero100582
  • 832
  • 1
  • 7
  • 13
  • 2
    are there any downsides to using this alternative? – invertigo Oct 27 '14 at 21:23
  • 3
    I've seen none, but I would recommend using Task for brand new code – Calimero100582 Nov 06 '14 at 16:51
  • Tasks cannot be used for e. g. a graceful shutdown implementation, which is why I favor the answer of Brian Gideon below (https://stackoverflow.com/a/4194938/2477582) that recommends using a CountdownEvent – Nicolas Jan 08 '19 at 14:45
  • If this is so simple why did Microsoft not implement it in WaitAll() ?? After reading all answers I still don't understand the stupid error message. Why does Microsoft tell us that it does not work with STA threads? What has the type of thead to do with waiting for an event at all? – Elmue Apr 30 '21 at 15:07
21

What about using the Tasks to do your threading for you.

http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx

var task1 = Task.Factory.StartNew(() => DoSomeWork());
var task2 = Task.Factory.StartNew(() => DoSomeWork());
var task3 = Task.Factory.StartNew(() => DoSomeWork());
Task.WaitAll(task1, task2, task3);
Oliver
  • 35,233
  • 12
  • 66
  • 78
12

Use one ManualResetEvent and wait on it. Also maintain a TaskCount variable that is set to the number of worker threads you start, use Interlocked.Decrement in the worker thread code as the very last action of the worker and signal the event if the counter reaches zero,e.g.

// other worker actions...
if (Interlocked.Decrement(ref taskCount) == 0)
   doneEvent.Set();
liggett78
  • 11,260
  • 2
  • 29
  • 29
  • Thanks it seems to be the only solution since WaitAll(..) doesn’t work. – Amir Rezaei Nov 16 '10 at 12:15
  • Yep, that would work. Just make sure to treat the main thread as if it were a work item and initialize `taskCount = 1` and do the `Decrement` and `Set` at the end of the `for` loop. – Brian Gideon Nov 16 '10 at 14:05
  • This is very similar to using the CountdownEvent. But the CountdownEvent has the advantage that you need only one variable instead of two like in your solution. – Elmue Apr 30 '21 at 15:02
10

I would refactor your code to use the CountdownEvent class instead.

private void Search() 
{ 
    const int CPUs = 2; 
    var done = new CountdownEvent(1);

    // Configure and launch threads using ThreadPool: 
    for (int i = 0; i < CPUs; i++) 
    { 
        done.AddCount();
        var f = new Indexer(Paths[i], doneEvents[i]); 
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              f.WaitCallBack(state);
            }
            finally
            {
              done.Signal();
            }
          }, i); 
    } 

    // Wait for all threads in pool  
    done.Signal();
    done.Wait();
    Debug.WriteLine("Search completed!"); 
} 
Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
1

use something like this:

foreach (ITask Task in Tasks)
{
    Task.WaitHandle = CompletedEvent;
    new Thread(Task.Run).Start();
}

int TasksCount = Tasks.Count;
for (int i = 0; i < TasksCount; i++)
    CompletedEvent.WaitOne();

if (AllCompleted != null)
    AllCompleted(this, EventArgs.Empty);
M. Jahedbozorgan
  • 6,914
  • 2
  • 46
  • 51