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

Popular posts from this blog

Write Unicode text using VBA

Calling Fortran intrinsic functions from Visual Basic

Dictionary class extensions (CopyTo, Sort) (C#)