Hook to a BackgroundWorker (C#)
Using the BackgroundWorker class (that resides in the System.ComponentModel namespace) is very convenient to use for parallel processing. In the following example I add a BackgroundWorker to split the work done in several experiment objects.
For that, I use an AddWorker function that creates a thread for each experiment. The line that "sticks" the application execution flow until all threads are completed, is the last one:
System.Threading.AutoResetEvent waitHandle;
public void Calculate()
{
//initialize the list of threads (BackgroundWorkers)
workers = new List<BackgroundWorker>();
//initialize the object that will wait during the execution process
waitHandle = new System.Threading.AutoResetEvent(false);
//add a thread for each experiment object
foreach (Experiment experiment in Experiments)
AddWorker(experiment);
//stick here until we use waitHandle.Set()
waitHandle.WaitOne();
}
The AddWorker() adds a thread for each experiment object. We use here one handler per event, for all threads.
private void AddWorker(Experiment experiment)
{
//initialize the worker thread
BackgroundWorker worker = new BackgroundWorker();
//add the worker to a local collection
workers.Add(worker);
//add the handlers
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
//run asynchronously by passing the 'experiment' object as a parameter
//the thread actually starts running from here
worker.RunWorkerAsync(experiment);
}
Then, we need to add the handlers that we have declared:
//this event will be triggered immediately after worker.RunWorkerAsync() is called
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//set the experiment object that we have passed in the RunWorkerAsync call
Experiment experiment = (Experiment)e.Argument;
//run the calculation here
experiment.Calculate();
//return the same object as a result
e.Result = experiment;
}
//store the number of completed threads
int iCompleted = 0;
//this event will be triggered after the completion of the DoWork event
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Experiment experiment = (Experiment)e.Result;
//if we arrive here Experiments.Count times, then all threads are finished
//by setting waitHandle.Set the Calculate() operation resumes
if (++iCompleted == Experiments.Count) waitHandle.Set();
}
There are many ways to hook to a thread until its operation is finished. I think that if we do not want to handle the BackgroundWorker events outside the module, then the "waitHandle" technique presented here is very convenient.
Comments