Click here to Skip to main content
15,921,174 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.6K   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 
Yes I am sure. I was surprised as you are. In provided example try to replace AsyncWorker with BackgroundWorker and replace the button1_Click function with this one:

private void button1_Click(object sender, EventArgs e) {
    while(true){
        for (int i = 0; i < 1000; i++) {
            if (!this.m_asyncWorker.IsBusy) {
                this.m_asyncWorker.RunWorkerAsync();
            }
            if (!this.m_bacgroundPrintWorker.IsBusy) {
                this.m_bacgroundPrintWorker.RunWorkerAsync();
            }
        }
        this.m_justDoSomething.Clear();
    }
}


Please could you tell me if the label1 will ever be set to "bw ended". In my case this never happened and I checked that m_asyncWorker_DoWork has truly been finished. I didn't happen, unless I implemented Application.DoEvents() loop. If you think that using a Application.DoEvents() loop is a better approach please continue with that, but I really don't like it.

AsyncClass also provides you the ability to control the number of AsyncCalls at the same time. I founded this very useful. With AsyncWorker implementation in my application I achieved very nice performance improvement. It helps me to control the background tasks, the number of them at the same time, and it helps me to control theirs importance. By setting the abortIfBusy to false, you have a "guarantee" that call will be done. If you set it to true and the worker is busy, it will just skipped this call.

You should be aware that between IsBusy and the actual call is a time gap, which means that some other AsyncCalls may fall in. And they will wait your task to finish. But in middle-time your task is not necessarily relevant any more. As I said in the article. If your are refreshing the screen there is no need to draw obsolete graphics. Just draw what is relevant to you.

I hope I answered your question. And as I said in the article, if I didn't use the background worker as I should please let me know.

Regards, Igor
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 

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.