I have 2 kinds of C# WPF app projects:
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:
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" 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
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.
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.