In c# .Net 3.5, how can I control multiple WaitHandles?
I have the following scenario:
My application must import several sets of data from another application, and time is critical. Because of that, it spawns one thread for each import. So, say I have imports 1 througth 10, where imports 4 and 5 can run only after import 1, 6 and 7 after import 2 and 8, 9 and 10 after import 3- Import 1
- Import 4
- Import 5
- Import 2
- Import 6
- Import 7
- Import 3
- Import 8
- Import 9
- Import 10
Searching around stackoverflow i found a few answers about waithandles but I'm not sure to control multiple waithandles.
I thougth about creating a list of import/handles and checking on them on a loop but i don't know how to fire the .set() from there. Or creating a thread for each father import and instantiate the handle inside it, and that father thread fires a thread for each child import, but handling threads of a thread is something i'm not sure is correct. Any ideias on how can i solve this ? UPDATE: After Brian Gideon's answer heres what i came up with:dateSince = Convert.ToDateTime(txtBoxDateSince.Text);
da开发者_StackOverflow中文版teTo = Convert.ToDateTime(txtBoxDateTo.Text);
//Loop all the days on the time interval
while (DateTime.Compare(dateSince, dateTo) <= 0)
{
foreach (ListItem father in checkBoxListFather.Items)
{
if (father.Selected == true)
{
processClass process = new processClass();
// This WaitHandle will be used to get the child tasks going.
var wh = new ManualResetEvent(false);
//Method to Import, wraped in a delegate
WaitCallback fatherMethod = new WaitCallback(process.importProcess);
//and its parameters
processClass.importParameters param = new processClass.importParameters(wh, father.Value, null, dateSince);
// Queue the parent task.
ThreadPool.QueueUserWorkItem(fundMethod, param);
// Register the child tasks.
foreach (ListItem child in checkBoxListChild.Items)
{
if (child.Selected == true)
{
processClass.importParameters parameters = new processClass.importParameters(null, child.Value, null, dateSince);
// Registers a callback for the child task that will execute once the
// parent task is complete.
WaitOrTimerCallback childMethod = new WaitOrTimerCallback(process.anotherImportProcess);
RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(wh, childMethod, parameters, Timeout.Infinite, true);
}//End if (child.Selected == true)
}//End foreach (ListItem fund in checkBoxListChild.Items)
}//End if (father.Selected == true)
}//End foreach (ListItem fundProcess in checkBoxListFather.Items)
dateSince = dtSince.AddDays(1);
}//End while (DateTime.Compare(since,to) < 0)
Pretty much the same answer, just used the methods without lambda expressions and used parameters on them.
I still didn't stress test it, but it's working pretty good.
Thanks Brian.Did you know
- WaitHandle.WaitAny Method
- WaitHandle.WaitAll Method
If the threads are simple linear in nature and you don't require queuing for scalability, you might be able to use C#: Waiting for all threads to complete
If you really want to dedicate one thread for each task then there is no need to use WaitHandle
instances at all.1 You could pass the Thread
instance, which is executing the parent task, to each child task and call Join
to make sure the parent task has finished.
void TaskEntryPoint(Thread parent, ImportTask task)
{
if (parent != null)
{
parent.Join(); // Wait for the parent task to complete.
}
task.Execute(); // Execute the child task.
}
Now all you need to do is get the parent tasks going on their own separate threads and then get the child tasks going on their own separate threads. When calling TaskEntryPoint
pass null
if it is for a parent task and pass the appropriate Thread
instance for each child task.
Update:
Based on your comment here is an example of how I might approach the problem using the ThreadPool
. This is a fairly advanced pattern using the ThreadPool.RegisterWaitForSingleObject
method. It also happens to be an extremely scalable solution since it uses the absolute minimum of resources to wait for a WaitHandle
to get signaled.
foreach (ImportTask parent in parentTasks)
{
// This WaitHandle will be used to get the child tasks going.
var wh = new ManualResetEvent(false);
// Needed to capture the loop variable correctly.
var p = parent;
// Queue the parent task.
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
// Execute the parent task.
p.Execute();
}
finally
{
// Signal the event so that the child tasks can begin executing.
wh.Set();
}
}, null);
// Register the child tasks.
foreach (ImportTask child in parent.ChildTasks)
{
// Needed to capture the loop variable correctly.
var c = child;
// Registers a callback for the child task that will execute once the
// parent task is complete.
RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(wh,
(state, to) =>
{
// Execute the child task.
c.Execute();
},
null, Timeout.Infinite, true);
}
}
The magic here is in the way RegisterWaitForSingleObject
waits on the event. It will register a callback that will be automatically executed once the event is signaled. But, it does it in such a manner that no thread in the pool is wasted on waiting for that event. Again, this a fairly sophisticated pattern that will require some thought on your part, but it will be very scalable.
1Starting a new thread for each task might not be the best strategy. Have you considered using the ThreadPool
or Task
classes?
精彩评论