Click here to Skip to main content
15,898,538 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.5K   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 
The guy had exactly the same problem.

Hi gang,

I'm using a separate thread to send commands to a TCP client so that if communications hang up for some reason, the UI running in the main thread is still responsive.

When I have a message to send, I call the background worker like this:

SmartRelayThread.RunWorkerAsync()



which runs this simple routine:



Public Class clsSRB
    Private WithEvents SmartRelayThread As New System.ComponentModel.BackgroundWorker ' The thread general communications will happen in.
    Private RelayBoardTCP As New ClientCom
    Private AsciiRcv As String     'Returned from board
    Private AsciiXmit As String    'To be sent to board
    Private Sub PanelMeterThread_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles SmartRelayThread.DoWork
        SmartRelayThread.WorkerSupportsCancellation = True
        ' Transmit the contents of AsciiXmit to the TCP client
        ' The ClientCommunication routine uses the TCP client example right from the help file (NetworkStream.Write).
        AsciiRcv = RelayBoardTCP.ClientCommunication(AsciiXmit)

    End Sub

It works great - the first time. In this case, I know for sure when it works because it causes a relay on my workbench to operate.

The problem is when I try to send another message, I get the "Background Worker is busy" message. It's ALWAYS busy.

Am I doing something wrong that causes the background worker to never finish? How can I dispose of this thread so that the background worker is available the next time I need to send a message?

I've tried cancelAsync and Dispose, but neither works.

Thanks,

Roger



And at the end he got the following answer:

The answer for you is probably to not use a BackgroundWorker at all. There's no rule that says you have to. BackgroundWorkers make simple multi-threading scenarios easier. If your scenario goes beyond the somple though, you don't try to force the BackgroundWorker to fit. You just don't use it. Either use the ThreadPool class explicitly, which the BackgroundWorker does implicitly, or else create a Thread object yourself. You can then just start a new thread each time instead of trying to use the same BackgroundWorker every time.


But it seems that the core of the problem is here[^].
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 

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.