I am having a bit of a conundrum here, and would like to know a couple of things:
- Am i doing this wrong?
- What is the expected behaviour of a backgroundworker in different scenarios...
- If possible, get an answer as to why i am getting specific behaviour would be nice...
For point 1, and ultimately 3 as well, i will explain what i am doing in Pseudo-Code so that you have the details without actually spitting out thousands of lines of code. While i write this post, i will look at the code itself to ensure that the information is accurate as far as when and what is happening. At the very end, i will also detail what is happening and why i am having issues.
Pseudo-Code details:
I have a main UI thread (WinForms form), where after selecting a few configuration options you click a button.
This button's event does some preliminary setup work in memory and on the file system to get things going and once that's done fires off ONE backgroundworker. This backgroundworker initializes 5 other backgroundworkers (form scope variables), sets their "Done" flags (bool - same scope) to true, sets their "Log" vars to a new List<LogEntry>
(same scope) and once that's done calls a method called CheckEndConditions
. This method call is done within the DoWork()
of the initial backgroundworker, and not in the RunWorkerCompleted
event.
The CheckEndConditions
method does the following logic:
- IF ALL "Done" vars are set to True...
- Grab the "Log" vars for all 5 BWs and adds their content to a master log.
- Reset the "Log" vars for all 5 BWs to a new
List<LogEntry>
- Reset the "Done" vars for all 5 BWs to False.
- Call
MoveToNextStep()
method which returns anEnum
value representative of the next step to perform - Based on the result of (5), grab a
List<ActionFileAction>
that needs to be processed - Check to ensure (6) has actions to perform
- If NO, set ALL "Done" flags to true, and call itself to move to the next step...
- If YES, partition this list of actions into 5 lists and place them in an array of
List<ActionFileAction>
calledThreadActionSets[]
- Check EACH partitioned list for content, and if none, sets the "Done" flag for the respective thread to true (this ensures there are no "end race scenarios")
- Fire off all 5 threads using
RunWorkerAsync()
(unless we are at the Finished step of course) Return
Each BW has the exact same DoWork()
code, which basically boils down to the following:
- Do i have any actions to perform?
- If NO, set my
e.Result
var to an empty list of log entries and exit. - If YES, loop for each action in the set and perform 4-5-6 below...
- What context of action am i doing? (Groups, Modules, etc)
- Based on (4), what type of action am i doing? (Add, Delete, Modify)
- Based on (5), perform the right action and log everything you do locally
- When all actions are done, set my
e.Result
var to the "log of everything i've done", and exit.
Each BW has the same RunWorkerCompleted()
code, which basically boils down to the following:
TRY
- From the
e.Result
var, grab theList<LogEntry>
and put it in my respective thread's "Log" var. - Set my respective "Done" var to true
- Call
CheckEndConditions()
CATCH
- Set my respective "Done" var to true
- Call
CheckEndConditions()
So that is basically it... in summary, i am splitting a huge amount of actions into 5 partitions, and sending those off to 5 threads to perform them at a faster rate than on a single thread.
The Problem
The problem i am having is that i often find myself, regardless of how much thought i put into this for race scenarios (specifically end ones), with a jammed/non-responsive program.
In the beginning, i had setup my code inefficiently and the problem was with End Race Scenarios and the threads would complete so fast that the last call made to CheckEndConditions
saw one of the "Done" vars still set to false, when in fact it wasn't/it had completed... So i changed my code to what you see above which, i thought, would fix the problem, but it hasn't. The whole process still jams/falls asleep, and no threads are actually running any processing when this happens which means that something went wrong (i think, not sure) with the last call to CheckEndConditions
.
So my 1st question: Am i doing this wrong? What is the standard way of doing what it is i want to do? The logic of what i've done feels sound to me, but it doesn't behave how i expect it to so maybe the logic isn't sound? ...
2nd question: What is the expected behaviour of a BW, when this scenario occurs:
An error occurred within the DoWork()
method that was un-caught... does it fire off the RunWorkerCompleted()
event? If not, what happens?
3rd question: Does anyone see something obvious as to why my problem is occurring?
Thanks for the help!