Reputation: 6312
I'm using the threadpool to (not surprisingly) manage a set of threads. What I am trying to do is get them to signal when done, I have:
ManualResetEvent[] doneEvents = new ManualResetEvent[char_set.Length];
public struct string_char
{
public string[] _str_char;
public ManualResetEvent _doneEvent;
public string_char(string[] str_char, ManualResetEvent doneEvent)
{
_str_char = str_char;
_doneEvent = doneEvent;
}
}
I have a loop here that creates an array of char and then I create an instance of my struct populating the char array and a done event:
doneEvents[no_of_elements - 1] = new ManualResetEvent(false);
string_char s_c = new string_char(array_holder, doneEvents[no_of_elements - 1]);
ThreadPool.QueueUserWorkItem(ThreadPoolCallback, s_c);
So, the thread is created, added to the pool, and it goes off merrily and runs, when it completes it sets the done event:
public void ThreadPoolCallback(Object s_c)
{
string_char _s_c = (string_char)s_c;
//do the calculations...
//when done:
_s_c._doneEvent.Set();
}
Back at the main loop the code is waiting here:
WaitHandle.WaitAll(doneEvents);
Console.WriteLine("All calculations are complete.");
The trouble is I keep getting the exception:
'WaitAll for multiple handles on a STA thread is not supported.'
I have looked this up on google but it doesn't really help, what am I doing wrong. This is basically a repeat of the ms msdn example, apart from I am using a struct rather than a class?
I fixed the problem, using the advice below, by switching to MTA (doh!) in the main; also it was useful to learn that the max is 64 thread count. So I am going to have to switch to a different wait model as the final app will be running a few more than that! Lots to learn.
Thanks.
Upvotes: 4
Views: 3116
Reputation: 16162
it is because at your Main() it defined with [STAThread]
not [MTAThread]
You can't use
WaitHandle.WaitAll()
on an STA thread as WaitAll is a blocking call , this is done to prevent the message pump to run. This is not allowed in Win32 and as such neither in .NET. Use one of the other Synchronization primitives like WaitOne or Thread.Join which do a limiited amount of pumping.
However a better choice is to use the new Task
to do what you want. here is similar example.
Upvotes: 2
Reputation: 48949
There are a couple of issues with using WaitHandle.WaitAll
in this manner. You have already discovered one of them. The other is that it is not very scalable since it has a 64 handle limit. Here is the pattern I use to wait for multiple work items to complete. It uses the CountdownEvent
class.
using (var finished = new CountdownEvent(1))
{
foreach (var workItem in workItemCollection)
{
var captured = item;
finished.AddCount();
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
ProcessWorkItem(captured);
}
finally
{
finished.Signal();
}
}, null);
}
finished.Signal();
finished.Wait();
}
Upvotes: 3
Reputation: 397
Try:
foreach(ManualResetEvent doneEvent in doneEvents)
WaitHandle.WaitOne(doneEvent);
Console.WriteLine("All calculations are complete.");
Upvotes: 0
Reputation: 11210
Either mark your thread MTA (provided it's not the UI thread which has to be STA) or use a different wait mechanism. For example, you could use one wait handle, with a task count:
int taskCount = 0;
// Launch a thread
Interlocked.Increment(ref taskCount);
// A thread terminates
if (Interlocked.Decrement(ref taskCount) == 0)
doneEvent.Set();
Upvotes: 0