By Tom


2012-09-13 20:51:48 8 Comments

In the past few days I have tested the new features of .net 4.5 and c# 5.

I like its new async/await features. Earlier I had used BackgroundWorker to handle longer processes in the background with responsive UI.

My question is: after having these nice new features, when should I use async/await and when a BackgroundWorker? Which are the common scenarios for both?

4 comments

@Peter Ritchie 2012-09-13 22:29:41

This is likely TL;DR for many, but, I think comparing await with BackgroundWorker is like comparing apples and oranges and my thoughts on this follow:

BackgroundWorker is meant to model a single task that you'd want to perform in the background, on a thread pool thread. async/await is a syntax for asynchronously awaiting on asynchronous operations. Those operations may or may not use a thread pool thread or even use any other thread. So, they're apples and oranges.

For example, you can do something like the following with await:

using (WebResponse response = await webReq.GetResponseAsync())
{
    using (Stream responseStream = response.GetResponseStream())
    {
        int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
    }
}

But, you'd likely never model that in a background worker, you'd likely do something like this in .NET 4.0 (prior to await):

webReq.BeginGetResponse(ar =>
{
    WebResponse response = webReq.EndGetResponse(ar);
    Stream responseStream = response.GetResponseStream();
    responseStream.BeginRead(buffer, 0, buffer.Length, ar2 =>
    {
        int bytesRead = responseStream.EndRead(ar2);
        responseStream.Dispose();
        ((IDisposable) response).Dispose();
    }, null);
}, null);

Notice the disjointness of the disposal compared between the two syntaxes and how you can't use using without async/await.

But, you wouldn't do something like that with BackgroundWorker. BackgroundWorker is usually for modeling a single long-running operation that you don't want to impact the UI responsiveness. For example:

worker.DoWork += (sender, e) =>
                    {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                    };
worker.RunWorkerCompleted += (sender, eventArgs) =>
                                {
                                    // TODO: do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

There's really nothing there you can use async/await with, BackgroundWorker is creating the thread for you.

Now, you could use TPL instead:

var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
                      {
                        int i = 0;
                        // simulate lengthy operation
                        Stopwatch sw = Stopwatch.StartNew();
                        while (sw.Elapsed.TotalSeconds < 1)
                            ++i;
                      }).ContinueWith(t=>
                                      {
                                        // TODO: do something on the UI thread, like
                                        // update status or display "result"
                                      }, synchronizationContext);

In which case the TaskScheduler is creating the thread for you (assuming the default TaskScheduler), and could use await as follows:

await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

In my opinion, a major comparison is whether you're reporting progress or not. For example, you might have a BackgroundWorker like this:

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (sender, eventArgs) =>
                            {
                            // TODO: something with progress, like update progress bar

                            };
worker.DoWork += (sender, e) =>
                 {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                    {
                        if ((sw.Elapsed.TotalMilliseconds%100) == 0)
                            ((BackgroundWorker)sender).ReportProgress((int) (1000 / sw.ElapsedMilliseconds));
                        ++i;
                    }
                 };
worker.RunWorkerCompleted += (sender, eventArgs) =>
                                {
                                    // do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

But, you wouldn't deal with some of this because you'd drag-and-drop the background worker component on to the design surface of a form--something you can't do with async/await and Task... i.e. you won't manually create the object, set the properties and set the event handlers. you'd only fill in the body of the DoWork, RunWorkerCompleted, and ProgressChanged event handlers.

If you "converted" that to async/await, you'd do something like:

     IProgress<int> progress = new Progress<int>();

     progress.ProgressChanged += ( s, e ) =>
        {
           // TODO: do something with e.ProgressPercentage
           // like update progress bar
        };

     await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                    {
                        if ((sw.Elapsed.TotalMilliseconds%100) == 0)
                        {
                            progress.Report((int) (1000 / sw.ElapsedMilliseconds))
                        }
                        ++i;
                    }
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

Without the ability to drag a component on to a Designer surface, it's really up to the reader to decide which is "better". But, that, to me, is the comparison between await and BackgroundWorker, not whether you can await built-in methods like Stream.ReadAsync. e.g. if you were using BackgroundWorker as intended, it could be hard to convert to use await.

Other thoughts: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html

@Trevor Elliott 2013-04-02 18:36:42

One flaw I think exists with async/await is that you may want to start multiple async tasks at once. await is meant to wait for each task to complete before starting the next one. And if you omit the await keyword then the method runs synchronously which is not what you want. I don't think async/await can solve a problem such as "start these 5 tasks and call me back when each task is done in no particular order".

@Peter Ritchie 2013-04-02 19:25:28

@Moozhe. Not true, you can do var t1 = webReq.GetResponseAsync(); var t2 = webReq2.GetResponseAsync(); await t1; await t2;. Which would await two parallel operations. Await is much better for asynchronous, but sequential tasks, IMO...

@Trevor Elliott 2013-04-03 19:02:34

You're right, but I had to omit using the await keyword in order to do so. Using await like in your example forces that the callbacks happen in the order you specify, which means that even if t2 finishes before t1 it will not execute its callback until t1 is finished.

@Trevor Elliott 2013-04-03 19:04:07

I definitely didn't know there was such flexibility with using awaitable functions that you can execute them asynchronously even without using an "async" keyword on the caller function. Good to know.

@Peter Ritchie 2013-04-03 19:24:58

@Moozhe yes, doing it that way maintains a certain sequence--as I mentioned. this is the main point of await is to get the asynchronisity in sequential-looking code. You could, of course, use await Task.WhenAny(t1, t2) to do something when the either task completed first. You'd likely want a loop to make sure the other task completes too. Usually you want to know when a specific task completes, which leads you to writing sequential awaits.

@Gennady Vanin Геннадий Ванин 2013-05-03 19:50:13

@Peter Ritchie 2013-05-15 12:56:53

Honesty, BackgroundWorker was never good for IO-bound operations.

@codea 2016-08-25 09:48:32

For your async/await example that reports progress, I needed to change var progress = new Progress<int>(); to IProgress<int> progress = new Progress<int>(); Otherwise the Report method is not exposed. I am using .Net 4.5. I think it may be good that you update your example.

@codea 2016-08-25 15:39:21

@PeterRitchie - this is still not quite right as when I reported it my delegate was not written yet! ProgressChanged is available only with the Progress<T>. So at the end I left the variable declaration as it was before and made a cast before calling "Report" --> ((IProgress<int>)(progress)).Report((int) (1000 / sw.ElapsedMilliseconds)); Sorry for the confusion!

@Gennady Vanin Геннадий Ванин 2013-05-03 19:43:31

BackgroundWorker is explicitly labeled as obsolete in .NET 4.5:

MSDN article "Asynchronous Programming with Async and Await (C# and Visual Basic)" tells:

The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than BackgroundWorker for IO-bound operations because the code is simpler and you don't have to guard against race conditions. In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool

UPDATE

  • in response to @eran-otzap's comment:
    "for IO-bound operations because the code is simpler and you don't have to guard against race conditions" What race conditions can occure , could you give an example ? "

This question should have been put as a separate post.

Wikipedia has a good explanation of racing conditions. The necessary part of it is multithreading and from the same MSDN article Asynchronous Programming with Async and Await (C# and Visual Basic):

Async methods are intended to be non-blocking operations. An await expression in an async method doesn’t block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method.

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active. You can use Task.Run to move CPU-bound work to a background thread, but a background thread doesn't help with a process that's just waiting for results to become available.

The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than BackgroundWorker for IO-bound operations because the code is simpler and you don't have to guard against race conditions. In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool

That is, "The async and await keywords don't cause additional threads to be created".

As far as I can recall my own attempts when I was studying this article a year ago, if you have run and played with code sample from the same article, you could bump in situation that its non-async versions (you could try to convert it to yourself) block indefinitely!

Also, for concrete examples you could search this site. Here are some example:

@Peter Ritchie 2013-05-04 14:29:36

BackgrondWorker is not explicitly labeled as obsolete in .NET 4.5. The MSDN article merely says that IO-bound operations are better with async methods--use of BackgroundWorker doesn't mean you can't use async methods.

@Gennady Vanin Геннадий Ванин 2013-05-04 15:50:45

@PeterRitchie , I corrected my answer. For me, "existing approaches are obsolete" is synonym to "The async-based approach to asynchronous programming is preferable to existing approaches in almost every case"

@Peter Ritchie 2013-05-04 18:06:36

I take issue with that MSDN page. For one, you do no more "coordination" with BGW than you do with Task. And, yes BGW was never intended to directly perform IO operstions--there'd always been a better way to do IO than in BGW. The other answer shows BGW is no more complex to use than Task. And if you use BGW correctly, there are no race conditions.

@eran otzap 2013-09-28 09:34:41

"for IO-bound operations because the code is simpler and you don't have to guard against race conditions" What race conditions can occure , could you give an example ?

@Gennady Vanin Геннадий Ванин 2013-09-29 00:50:38

@eran-otzap , please check "UPDATE" in my answer. You should have put your comments as a question instead of duplicating your comments. The eamples are simply non-asyc versions of a sample from the same article that you quoted

@ZunTzu 2015-10-27 18:43:23

This answer is wrong. Asynchronous programming can too easily trigger deadlocks in non-trivial programs. In comparison BackgroundWorker is simple and rock solid.

@TommyN 2012-09-13 21:30:15

This is a good introduction: http://msdn.microsoft.com/en-us/library/hh191443.aspx The Threads section is just what you are looking for:

Async methods are intended to be non-blocking operations. An await expression in an async method doesn’t block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method.

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active. You can use Task.Run to move CPU-bound work to a background thread, but a background thread doesn't help with a process that's just waiting for results to become available.

The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than BackgroundWorker for IO-bound operations because the code is simpler and you don't have to guard against race conditions. In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool.

@eran otzap 2013-09-28 09:35:03

"for IO-bound operations because the code is simpler and you don't have to guard against race conditions" What race conditions can occure , could you give an example ?

@Servy 2012-09-13 20:55:11

async/await is designed to replace constructs such as the BackgroundWorker. While you certainly can use it if you want to, you should be able to use async/await, along with a few other TPL tools, to handle everything that's out there.

Since both work, it comes down to personal preference as to which you use when. What is quicker for you? What is easier for you to understand?

@Tom 2012-09-13 20:58:28

Thanks. For me async/await seems much more clear and 'natural'. BakcgoundWorker makes the code 'noisy' in my opinion.

@Servy 2012-09-13 20:59:02

@Tom Well, that's why Microsoft spent a lot of time and effort implementing it. If it wasn't any better they wouldn't have bothered

@usr 2012-09-13 21:12:41

Yes. The new await stuff makes the old BackgroundWorker appear to be totally inferior and obsolete. The difference is that dramatic.

@Stephen Cleary 2012-09-14 00:17:45

I've got a pretty good rundown on my blog comparing different approaches to background tasks. Note that async / await also allows asynchronous programming without thread pool threads.

@Quango 2014-06-18 13:51:41

Downvoted this answer, it's misleading. Async/await is NOT designed to replace background worker.

@Servy 2014-06-18 14:06:24

@Quango Of course it is. It's not just designed to do that one thing, but one of the primary use cases for async/await when it was under development is assisting in the exact situation that BGW was designed to handle, namely performing some work in a UI application asynchronously and updating the UI when the work completes.

@xr280xr 2015-09-06 23:26:04

It's not a 1 to 1 replacement because async/await doesn't perform the operations on a separate thread. BackgroundWorker allowed a task to truly be run in parallel where async/await simply delegates the order of the work. Now whether you really need a separate thread is up to you.

@Servy 2015-09-08 13:11:22

@xr280xr When you add in the rest of the TPL then it does in fact provide all of that functionality. Specific to what you're referring to, Task.Run is all you'd need to create a Task representing CPU bound work done in another thread. I specifically said in the question that async/await combined with the TPL was capable of handling all of the functionality of a BGW.

@ccalboni 2018-11-23 14:32:40

It is designed to replace BackgroundWorker for those tasks where BackgroundWorker was used as a workaround to make the application responsive. It is NOT a replacement for true background works, those works that need to run behind the scenes, maybe notifying the user from time to time (an automatic saving, a download etc).

@Servy 2018-11-26 14:22:27

@ccalboni And why do you think the TPL can't be used for those types of use cases? What features does it not provide that would be needed to solve those problems?

@ccalboni 2018-11-30 09:08:34

It may sound stupid, but I had to move to BGW because I wasn't able to move a progressbar while an async operation was ongoing @Servy

@Servy 2018-11-30 14:20:18

@ccalboni That's what the Progress class exists to do.

Related Questions

Sponsored Content

17 Answered Questions

[SOLVED] Using async/await with a forEach loop

7 Answered Questions

[SOLVED] async/await - when to return a Task vs void?

12 Answered Questions

[SOLVED] Do HttpClient and HttpClientHandler have to be disposed?

21 Answered Questions

[SOLVED] How and when to use ‘async’ and ‘await’

6 Answered Questions

[SOLVED] HttpClient.GetAsync(...) never returns when using await/async

5 Answered Questions

[SOLVED] Using async/await for multiple tasks

3 Answered Questions

[SOLVED] Wasn't it .NET 4.0 TPL that made APM, EAP and BackgroundWorker asynchronous patterns obsolete?

3 Answered Questions

Sponsored Content