Click here to Skip to main content
16,005,697 members
Articles / Programming Languages / C#

A Simple Way To Use Asynchronous Call in Your Multithreaded Application

Rate me:
Please Sign up or sign in to vote.
2.75/5 (8 votes)
13 Apr 2017CPOL2 min read 33.8K   604   30   6
A simple way to use asynchronous calls

Introduction

Running asynchronous tasks can be a nightmare, especially for beginners. In the attached code, you will find a simple AsyncWorker class that can make your life easier, even if you don't know all the background. I would really like to see your contribution to the provided code, especially to the ReportProgress function.

Background

Why did I write this helper class? Simply because I didn't like the .NET BackgroundWorker class and all the issues related with the IsBusy parameter. Sometimes IsBusy does not transit from true to false even if you successfully end the async task. The backgroundWorker forces you to use Application.DoEvents inside a while loop. That's craziness to me. Maybe there is some other way to use it correctly, but I did not find out how. I tried to follow the same design as BackgroundWorker to make replacement as simple as possible.

Using the Code

If you look at the BackgroundWorker documentation, then it should be trivial for you. To initialize the AsyncWorker, simply define:

C#
AsyncCallback m_asyncWorker;		 

And somewhere in your code (constructor), initialize it.

C#
//By setting the maximumCount, we can simulate the simple ThreadPool.
//The maximumCount parameter tells you how many concurrent threads may be
//started simultaneously. Others will wait or be rejected if abortIfBusyParameter
//is set to true.
this.m_asyncWorker = new AsyncWorker(1);

//assign a background task
this.m_asyncWorker.DoWork += new DoWorkEventHandler(m_asyncWorker_DoWork);

Your background task might be anything you like:

C#
void m_asyncWorker_DoWork(object sender, DoWorkEventArgs e) {
    Console.WriteLine("Hello world! I was started asynchronously.");
}  

And finally to call the background worker, simply type:

C#
//because abortIfBusyParameter is set to true, RunWorkerAsync will
//return false (abort the call) if the previous call is still running. 
if (!this.m_asyncWorker.RunWorkerAsync(true)) {
	Console.WriteLine("Worker in use....");
}

By setting RunWorkerAsync parameter abortIfBusy to true, you can skip this event if the previous async call is still running. By setting it to false, it will wait in a queue and will be fired immediately after IsBusy switches from true to false. This mechanism is very helpful in performance sensitive applications. If you have many "low priority" events that start your AsyncCalls and in case that the previous async call is still running (IsBusy), you can decide if another async call should be started or not.

For example: If you are dragging an object on the screen, you do not need to repaint it every time. Just paint it when IsBusy is false. If you would like a smoother effect, simply increase the maximumCount (your thread pool).

At the end, if you need a callback, subscribe yourself to the RunWorkerCompleted event.

C#
//assign to a RunWorkerCompleted. It is your callback event.
this.m_asyncWorker.RunWorkerCompleted += 
	new RunWorkerCompletedEventHandler(m_asyncWorker_RunWorkerCompleted);

Points of Interest

Hope this tool will make your life easier now. Please do not ask about locking in JustDoSomething class. It was part of some other test.

History

  • 6 October, 2009 - First release (Igor Alfirevic)

License

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


Written By
Slovenia Slovenia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Vyacheslav Trubarov13-Oct-09 1:54
Vyacheslav Trubarov13-Oct-09 1:54 
GeneralRe: My vote of 1 Pin
gico13-Oct-09 8:07
gico13-Oct-09 8:07 
Question"Sometimes IsBusy does not transit from true to false even if you successfully end the async task." Pin
f r i s c h12-Oct-09 2:16
f r i s c h12-Oct-09 2:16 
AnswerRe: "Sometimes IsBusy does not transit from true to false even if you successfully end the async task." Pin
gico13-Oct-09 3:59
gico13-Oct-09 3:59 
GeneralRe: "Sometimes IsBusy does not transit from true to false even if you successfully end the async task." Pin
f r i s c h13-Oct-09 4:13
f r i s c h13-Oct-09 4:13 
GeneralRe: "Sometimes IsBusy does not transit from true to false even if you successfully end the async task." Pin
gico13-Oct-09 7:21
gico13-Oct-09 7:21 
Hi! I am glad of your comments.

I completely agree with yours first point. I write it just as an example not as a template that should be used Smile | :) . I put an indefinite loop just to show you an example in which BackgroundWorker will never transit IsBusy state from true to false.

Regarding point 2. Again, it is not mentioned to run the BackgroundWorker in a loop. But for instance try to imagine a scenario, in which you are receiving an event. And this event is very frequent. For instance you are getting sensor information. And your event is triggered 10 times per second. On each triggered event you need to redraw your form. And if you redraw takes 150 miliseconds, you will very soon end in event queue. Even when you will disconnect your sensor, you will still redrawing on the form, until your event queue will not be empty.

Now there are many ways to reduce number of triggered events but I would like to avoid that. I tried to solve a similar problem with a BackgoundWorker but I run into IsBusy problem. Why? Because when you have a huge number of events that calls a same event handler function, you will run into very similar situation as it was in a while loop case. Your BackgroundWorker does not transit IsBusy to false (because of Thread starving ???).

As you said in point 2, the only way in this scenario would be to use Threads. Of course that is not a bad idea. But with threads your world gets a little bit complicated. You need to have a ThreadPool to limit the number of simultaneously executing events and on the end you still need a signalization that would inform you if thread was successfully finished.

To make it simple I built the AsyncWorker that has almost the same design as BackgroundWorker (and I like its design), but in the background its implementation is based on .NET asynchronous call. With that I have avoided using threads (asynchronous call in it is basic, should be a thread), and I can use it in scenarios, where the same background task should be called very frequently. In case the background worker is busy you can always decide if the action on the triggered event will be execute or you will spare CPU cycles and wait for the next round.

Regards, Igor

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.