I am currently recreating functionality of something with a GUI that works in and, but through a terminal interface. So the error is not on the other end with the events triggering, because it works in original GUI form.
I run a task composed of subtasks on multiple machines.
I subscribe to events which trigger when progress is made and print out a descriptive message. A message is to be printed out for each X subtasks for all Y machines.
Asynchronous multithreading operation then occurs.
I would like to print out a message for each subtask for each machine being resolved only once.
I track completion of the subtasks, and keep a 2D boolean array where rows is machines and columns subtasks.
When debugging I can see that the event handler methods below are being entered. The print statement in numOfSubtasksFoundEventHandler is run, but before I reach to set the AutoReset event multiple BigTask events are triggered, blocked at the .WaitOne.
However despite the fact that numOfSubtasksFound.Set() is run later, nothing else is printed nor does the program finish execution. Nothing gets past the numOfSubtasksFound.WaitOne s.
If I take out the numOfSubtasksFound.WaitOne in the BigTaskHandler method I receive similar behavior but a few messages stating the BigTask completes and then the program stalls somewhere else.
What is the best way to manage the blocking and unblocking here or is there a small fix?
What I need is a way to block the operation of the subtask event handler method until numOfSubtasksFoundEventHandler has run once. I only need numOfSubTasksFoundEventHandler to run once only.
Currently the subtask event handler is not being unblocked properly. The switch case code is never executed after numOfSubtasksFound.Set(); is run.
//MAIN
bool[] machinesDoneTasks = new bool[numOfMachines];
bool[][] machinesDoneSubtasks = new bool[numOfMachines][];
try
{
//thread/task blocking
numOfSubtasksFound = new AutoResetEvent(false);
AllSubTasksDone = new AutoResetEvent(false);
AllBigTasksDone = new AutoResetEvent(false);
//Subscribe to events to get number of subtasks and print useful information as tasks progress
numOfSubtasksFoundEvent += numOfSubtasksFoundEventHandler;
SubTaskProgEvent += SubTaskEventProgHandler; //prog stands for progress
BigTaskProgEvent += BigTaskProgEventHandler;
RunAllTasksOnAllMachines();//this will trigger the events above
//Don't exit program until those descriptive messages have been printed
numOfSubtasksFound.WaitOne();
AllSubTasksDone.WaitOne();
//SubTaskProgEvent -= SubTaskProgEventHandler;
AllBigTasksDone.WaitOne();
//BigTaskProgEvent -= BigTaskProgEventHandler;
}
catch (Exception e)
{
//print exceptions
}
//END MAIN
Below is not necessarily the 1st event to be triggered.
internal void numOfSubtasksFoundEventHandler(object sender, EventArgs e)
{
//get number of subtasks from args after checking for nulls, empty arrays
for (int i = 0; i < numOfSubtasks; i++)
machinesDoneSubtasks[i] = new bool[numOfSubtasks];
Console.WriteLine("number of subtasks found");
numOfSubtasksFoundEvent -= numOfSubtasksFoundEventHandler;//don't subscribe to event where we get this from anymore
if (numOfSubtasksFound != null)
numOfSubtasksFound.Set(); //stop blocking
}
Subtask events do not necessarily get processed before big task events.
internal void SubtaskEventProgHandler(object sender, EventArgs e)
{
//null, empty checks on args
//Wait until we know how many subtasks there are and the 2D boolean array is fully built
numOfSubtasksFound.WaitOne();
switch (e.WhatHappened)
{
Case.TaskComplete:
Console.Write(e.Machine + " is done subtask " + e.subTask);
//logic to determine machine and subtask
machinesDoneSubtasks[machine][Subtask] = true;
if (AllSubTasksDone != null && machinesDoneSubtasks.OfType<bool>().All(x => x))
AllSubTasksDone.Set(); //stop blocking when 2D array is all true
break;
//other cases, different prints, but same idea
}
}
BigTask progress events occur at the beginning middle and end of processing. I only print out the details of the Cases I want.
internal void BigTaskProgEventHandler(object sender, EventArgs e)
{
//Wait until we know how many subtasks there are and the 2D boolean array is fully built before printing
numOfSubtasksFound.WaitOne();
//null, empty exception checks
switch (e.WhatHappened)
{
Case.TaskComplete:
Console.Write(e.Machine + " is done task " + e.subTask);
//logic to determine machine
machinesDoneTasks[machine] = true;
if (AllBigTasksDone != null && machinesDoneTasks.All(x => x))
AllBigTasksDone.Set();
break;
}
//other cases, different prints, but same idea
}
My issue was that after the 1st event was triggered, other subtask event handler event would call .WaitOne which would block. This could happen after the number of subtasks is discovered. The issue then was the .Set would only be called once, and it would never be unblocked.
So using a boolean flag to be set when the number of subtasks is discovered, and locking the sub task event handler was the way to go.