19

I have 2 kinds of C# WPF app projects:

  • based on .NET 4.0 that I cannot migrate to .NET 4.5
  • based on .NET 4.0 that I can migrate to .NET 4.5

All of them should spawn 2-10 long-running (days) processes which can be cancelled and re-launched by users.

I am interested to follow the best design practices. First of all, now, I am interested to disambiguate about BackgroundWorker usage though, I hope, my question should be valid about other asynchronous patterns.

I see (contradicting) concurrent points of view about

asynchronous patterns:

"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"

I am still in doubt:

  1. Are those patterns (first of all, BGW) obsolete in .NET 4.5 ?
  2. If they are obsolete in .NET 4.5 why aren't they obsolete in .NET 4.0?

    2A) Do I understand incorrectly that .NET 4.5 new features are still "easy" implementable/reproducible in .NET 4.0?

Community
  • 1
  • 1

3 Answers3

15

I consider these patterns (APM, EAP, and BGW in particular) obsolete in .NET 4.5. The combination of async with Task.Run is superior to BGW in every way. In fact, I just started a series on my blog where I will compare BGW to Task.Run and show how it's more cumbersome in every situation; there are some situations where it's just slightly more cumbersome, but there are other situations where it's much more cumbersome.

Now, whether they're obsolete in .NET 4.0 is another question entirely. From your other posts, you're talking about developing for .NET 4.0 with VS2010, so the backport Microsoft.Bcl.Async isn't an option. In that case, neither APM nor EAP can be considered obsolete IMO. On this platform, you can consider Task.Factory.StartNew as an alternative to BGW but BGW does have some advantages when it comes to progress reporting and automatic thread marshaling of its progress and completion events.

Update: I did recently update an old blog post of mine where I discuss various implementations of background operations. In that post, when I talk about "Tasks (Async Methods)", I mean using Task with all the .NET 4.5 async support, Task.Run, etc. The "Tasks (Task Parallel Library)" section is evaluating Task as it existed in .NET 4.0.

prime23
  • 3,283
  • 1
  • 32
  • 49
Stephen Cleary
  • 376,315
  • 69
  • 600
  • 728
  • 4
    I generally recommend Task and/or await if using .NET 4.5. But Task & BGW have 2 distinct scenarios. Task is good for general short asynchronous tasks that could be chained to a continuation and await is good suck tasks implicitly marshalling back to the UI thread. BGW is good for a single long operation that shouldn't affect the responsiveness of your UI. You can drag-drop a BGW onto design surface and double-click to create event handlers. You don't have to deal with LongRunning or ConfigureAwait if you don't want to marshal to another thread. many find BGW progress easier than IProgress – Peter Ritchie May 03 '13 at 20:24
  • @Peter-Ritchie , please put your comment as the answer. I am interested in long-running ops scenarios and I couldn't clearly grasp the explanations and rationale of "obsolete" proclamations as well as WHEN, WHY and WHERE. It is highly confusing especially in context of new concepts – Gennady Vanin Геннадий Ванин May 04 '13 at 11:57
  • 1
    @ГеннадийВанинНовосибирск: `Task.Run` works perfectly well for long-running tasks, particularly in desktop applications. `LongRunning` and `ConfigureAwait` are both unnecessary in this scenario. – Stephen Cleary May 04 '13 at 13:15
  • But background workers can be dragged onto the designer from the toolbox, right? Do `async` and `Task.Run` replicate that functionality? – Kyle Delaney Sep 18 '17 at 19:20
  • @KyleDelaney: No. `async` and `Task.Run` expect you to write code. – Stephen Cleary Sep 18 '17 at 20:25
15

I generally recommend Task and/or await if using .NET 4.5. But Task & BGW have 2 distinctly different scenarios. Task is good for general short asynchronous tasks that could be chained to a continuation and await is good at tasks implicitly marshalling back to the UI thread. BGW is good for a single long operation that shouldn't affect the responsiveness of your UI. You can drag-drop a BGW onto design surface and double-click to create event handlers. You don't have to deal with LongRunning or ConfigureAwait if you don't want to marshal to another thread. Many find BGW progress easier than IProgress<T>.

Here's some examples of using both in a "lengthy operation" scenario:

Since the question specifically mentions .NET 4.0, the following is simple code that uses a Task to do a lengthy operation while providing progress to a UI:

startButton.Enabled = false;
var task = Task.Factory.
                StartNew(() =>
                    {
                        foreach (var x in Enumerable.Range(1, 10))
                        {
                            var progress = x*10;
                            Thread.Sleep(500); // fake work
                            BeginInvoke((Action) delegate { 
                               progressBar1.Value = progress; 
                            });
                        }
                    }, TaskCreationOptions.LongRunning)
                .ContinueWith(t =>
                    {
                        startButton.Enabled = true;
                        progressBar1.Value = 0;
                    });

Similar code with BackgroundWorker might be:

startButton.Enabled = false;
BackgroundWorker bgw = new BackgroundWorker { WorkerReportsProgress = true };
bgw.ProgressChanged += (sender, args) => 
    { progressBar1.Value = args.ProgressPercentage; };
bgw.RunWorkerCompleted += (sender, args) =>
{
    startButton.Enabled = true;
    progressBar1.Value = 0;
};
bgw.DoWork += (sender, args) =>
{
    foreach (var x in Enumerable.Range(1, 10))
    {
        Thread.Sleep(500);
        ((BackgroundWorker)sender).ReportProgress(x * 10);
    }
};
bgw.RunWorkerAsync();

Now, if you were using .NET 4.5 you could use Progress<T> instead of the BeginInvoke call with Task. And since in 4.5, using await would likely be more readable:

startButton.Enabled = false;
var pr = new Progress<int>();
pr.ProgressChanged += (o, i) => progressBar1.Value = i;
await Task.Factory.
            StartNew(() =>
                        {
                            foreach (var x in Enumerable.Range(1, 10))
                            {
                                Thread.Sleep(500); // fake work
                                ((IProgress<int>) pr).Report(x*10);
                            }
                        }, TaskCreationOptions.LongRunning);
startButton.Enabled = true;
progressBar1.Value = 0;

Using Progress<T> means the code is not coupled to a specific UI framework (i.e. the call to BeginInvoke) in much the same way that BackgroundWorker facilitates decoupling from a specific UI framework. If you don't care, then you don't need to introduce the added complexity of using Progress<T>

As to LongRunning, as Stephen Toub says: "You'd typically only use LongRunning if you found through performance testing that not using it was causing long delays in the processing of other work" so, if you find you need to use it, then you use it--there's the added analysis or just the "complexity" of always adding the LongRunning parameter. Not using LongRunning means the thread pool thread used for the long running operation won't be usable for other, more transient, tasks and could force the thread pool to delay starting one of these transient tasks while it starts up another thread (at least a second).

There's no attributes in the framework that specifically say that BGW (or EAP, or APM) are deprecated. So, it's up to you to decide where and when any of these things are "obsolete". BGW in particular always had a very specific usage scenario that still applies to it. You have fairly decent alternatives in .NET 4.0 and 4.5; but I don't really think BGW is "obsolete".

I'm not saying always use BackgroundWorker, I'm just saying think before you automatically deprecate BackgroundWorker, in some cases it might be a better choice.

Peter Ritchie
  • 33,368
  • 9
  • 74
  • 96
  • 1
    Dunno why all answers are shifted to .NET 4.5 though the question is specifically targeted to .NET 4.0 and for quite clear&obvious reasons&aims - to facilitate&shortcut the analysis, refractoring & design decisions in 4.0 The related important questions, like [What can I do in C# 5 with .Net 4.5 that I couldn't do in C# 4 with .Net 4?](http://stackoverflow.com/questions/11548373/what-can-i-do-in-c-sharp-5-with-net-4-5-that-i-couldnt-do-in-c-sharp-4-with-n) are being trolled and closed. Though, are not so obvious taking into account that .NET 4.5 cannot be installed/run on Windows XP – Gennady Vanin Геннадий Ванин May 05 '13 at 13:00
  • @Геннадий-Ванин Probably due to the fact the OP suggests certain things are deemed obsolete in 4.5 (which they technically are not). – Peter Ritchie Jun 10 '13 at 18:13
  • Slightly expanding on this, if the work you want to do involves an event notifying of the changes you can close over the `Progress<>`variable `processor.ItemProcessed += (sender, e1) => ((IProgress) p).Report(e1.NextItem);` and it `Just works ™` – Chris Marisic Nov 06 '14 at 16:22
  • How do you drag and drop a background worker onto a design surface? I've looked this up and it's supposed to be in the components tab of the toolbox, but that tab isn't showing up. – Kyle Delaney Sep 18 '17 at 19:12
  • 1
    @KyleDelaney In a WinForms app? I believe `BackgroundWorker` is only available to a WinForm form. – Peter Ritchie Sep 21 '17 at 19:45
  • Oh, I was trying to do it in WPF. – Kyle Delaney Sep 21 '17 at 19:57
1

My question was:

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

i.e. a doubt, asking for confirmation or negation that if something is obsolete in .NET 4.5, then I do not see why and how it can be different in .NET 4.0, i.e. with Task Parallel Library (without async/await engagement and .NET 4.5 support exclusively)

And I am quite happy to find Nick Polyak's answers in his codeproject article "Task Parallel Library and async-await Functionality - Patterns of Usage in Easy Samples" stated:

  • "Much of .NET 4.5 functionality has been changed to Tasks and BackgroundWorker is all but obsolete now, but you still might come across some instances when it is necessary to use the EAP pattern. In such cases, it will be nice to produce Task objects out of the EAP functionality so that you'll be able to schedule them to run in parallel or wait for a number of previous Tasks to finish etc. In this section we show how to achieve it"
  • "Having Task functionality made BackgroundWorker functionality largerly obsolete - you can achieve whatever you need using a Task instead of a BackgroundWorker. Still there might be some reason you want to use BackgroundWorker functionality on the team - whether because your legacy code uses it or because most of your team members or your boss love it and understand it better than the newer Task functionality"

still being open to see any different argumented points of view

  • 2
    `if something is obsolete in .NET 4.5, then I do not see why and how it can be different in .NET 4.0` - I define "obsolete" as "having no advantages when compared to an alternative". So BGW is not obsolete in .NET 4.0 because (in some situations) it is easier/simpler than using `Task`, `TaskScheduler`, etc. So it has the advantage of simplicity (in some situations), hence is not obsolete. But it is obsolete in .NET 4.5 because the new `await` functionality makes all that easier for tasks; there are no advantages that BGW has over `await Task.Run`, so I consider it obsolete in .NET 4.5. – Stephen Cleary May 05 '13 at 14:25
  • I should say "no advantages" *except* it can be used in form designers (it exists in the toolbox, can be dragged/dropped onto a form/service, and you can double-click to add event handlers). I never used that functionality so I tend to overlook it. :) – Stephen Cleary May 05 '13 at 14:31
  • 1
    @StephenCleary, BGW component exists in toolbox only for "Windows Forms Applications" but not for "WPF Application" projects. As you can guess I am using Async CTP in .NET 4.0 and eventually almost all my doubts boil dowm to [What are the major risks vs. benefits of using VS2010 Async CTP?](http://stackoverflow.com/questions/15873354/what-are-the-major-risks-vs-benefits-of-using-vs2010-async-ctp) and what is so drastically different in .NET 4.5 that I would not be able to reproduce in .NET 4.0 with Async CTP? – Gennady Vanin Геннадий Ванин May 05 '13 at 14:48