Article scope
- Provide a swift review of available async design patterns.
- Introduce a background threads manager using the nice
BackgroundWorker
class.
Introduction to secretly doing bad things without the UI knowing them
Basically, as MSDN informs us, there are two ways of implementing asynchronous operations:
- Using the
IAsyncResult
objects. - Using events.
Both of them are design patterns easy to implement. But not long ago, a nice class has been provided to us to please our never ending needs for a less work – more results component. It’s called BackgroundWorker
and it’s cool! It allows you to run operations on separate threads, far from the needy UI.
BackgroundWorker in action
Here are the steps you need to take to use BackgroundWorker
:
- Create a
BackgroundWorker
instance. - Set its properties
WorkerReportProgress
and WorkerSupportsCancellation
to true
or false
. - Add event handlers to its
DoWork
, ProgressChanged
, and RunWorkerCompleted
events. - Handle those events.
- Call
RunWorkerAsync
with or without a parameter.
Limitations, as provided by MSDN:
- Do not manipulate any UI object in the
DoWork
event handler. Do any UI manipulation in the ProgressChanged
and RunWorkerCompleted
events. - Events are not marshaled across AppDomain boundaries.
So why would I need a manager?
Well, it mainly depends on your application’s necessities. If you want to load data from a server and present it to the user, then an implementation of the BackgroundWorker
would be more than welcome. I mean, why over generalize stuff? It does no good for developers. But, what if every action a user performs demands the use of asynchronous calls? I don’t want multiple instances of BackgroundWorker
in my code or other semi-professional implementations of it. So, let’s couple the action I need to perform and the methods that help me succeed in loading, processing, and getting the data to the UI (the asynchronous workflow).
The BackgroundThreadsManager
It’s a generic class that is supposed to get an enumeration of actions. For example:
public enum Actions {
Create,
Save,
Generate
}
Adding an action to this manager means assigning a workflow to one value of that enumeration. An asynchronous workflow is a wrapper around the BackgroundWorker
that does certain calls based on the current action. An example is provided below.
Using the manager
So, what should I code?
- Create an enumeration with all the actions a user would do.
public enum Actions
{
Create
}
- Create an instance of
BackgroundThreadsManager
.
asyncMan = new BackgroundThreadsManager<Actions>();
- Create a
BackgroundThreadWorkflow
for each action in the enumeration and add them to the manager.
var createDataWorkflow =
new BackgroundThreadWorkflow<Actions>(Actions.Create);
asyncMan.AddAction(createDataWorkflow);
- Add event handlers to the manager.
asyncMan.StartCalled += new
BackgroundThreadsManager<Actions>.CallerHandler(asyncMan_StartCalled);
asyncMan.CallCompleted += new
BackgroundThreadsManager<Actions>.CallbackHandler(asyncMan_CallCompleted);
asyncMan.ReportCalled += new
BackgroundThreadsManager<Actions>.ReportHandler(asyncMan_ReportCalled);
- Handle those events.
void asyncMan_StartCalled(object sender,
Actions action, DoWorkEventArgs e)
{
switch (action)
{
case Actions.Create:
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(int.Parse(e.Argument.ToString()));
asyncMan.ReportData(Actions.Create,
new BackgroundThreadReportData<Actions>(action, i));
}
e.Result = "Done creating data!!!";
break;
default:
break;
}
}
void asyncMan_ReportCalled(object sender, BackgroundThreadReportData<Actions> e)
{
switch (e.Action)
{
case Actions.Create:
string str = e.Argument.ToString();
pb.Value = int.Parse(str);
break;
default:
break;
}
}
void asyncMan_CallCompleted(object sender, Actions action,
RunWorkerCompletedEventArgs e)
{
switch (action)
{
case Actions.Create:
MessageBox.Show(e.Result.ToString());
break;
default:
break;
}
}
- Call an action (with or without an argument) when that user demands it.
asyncMan.CallAction(new
BackgroundThreadInputData<Actions>(Actions.Create, 10.0));
Conclusion
Having a simpler and nicer way to manage asynchronous calls sure makes my day when I have bigger and more demanding tasks to accomplish. And it’s true, handling those types of calls should not be of great complexity and difficulty. If they are, you’re doing something wrong.
I hope those lines of code will aid somebody in their quest to find simple and beautiful ways to manage rather mundane tasks.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.