Click here to Skip to main content
15,904,416 members
Articles / Programming Languages / C#

Hide! Hide in the back!

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
2 Jul 2009CPOL2 min read 18.6K   157   7   1
An idea for a background threads manager.

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:

  1. Using the IAsyncResult objects.
  2. 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:

  1. Create a BackgroundWorker instance.
  2. Set its properties WorkerReportProgress and WorkerSupportsCancellation to true or false.
  3. Add event handlers to its DoWork, ProgressChanged, and RunWorkerCompleted events.
  4. Handle those events.
  5. 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:

C#
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?

  1. Create an enumeration with all the actions a user would do.
  2. C#
    public enum Actions
    {
        Create
    }
  3. Create an instance of BackgroundThreadsManager.
  4. C#
    asyncMan = new BackgroundThreadsManager<Actions>();
  5. Create a BackgroundThreadWorkflow for each action in the enumeration and add them to the manager.
  6. C#
    var createDataWorkflow = 
        new BackgroundThreadWorkflow<Actions>(Actions.Create);
    asyncMan.AddAction(createDataWorkflow);
  7. Add event handlers to the manager.
  8. C#
    asyncMan.StartCalled += new 
      BackgroundThreadsManager<Actions>.CallerHandler(asyncMan_StartCalled);
    asyncMan.CallCompleted += new 
      BackgroundThreadsManager<Actions>.CallbackHandler(asyncMan_CallCompleted);
    asyncMan.ReportCalled += new 
      BackgroundThreadsManager<Actions>.ReportHandler(asyncMan_ReportCalled);
  9. Handle those events.
  10. C#
    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;
        }
    }
  11. Call an action (with or without an argument) when that user demands it.
  12. C#
    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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionUsing a enumeration for handling the right action? Pin
Polity6-Jul-09 12:17
Polity6-Jul-09 12:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.