I have 2 kinds of C# WPF app projects:
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:
A) .NET 4.5 made them obsolete
"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"
BackgroundWorker
) are not obsolete in .NET 4.5 I am still in doubt:
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?
BackgroundWorker is explicitly labeled as obsolete in . NET 4.5: in the book By Joseph Albahari, Ben Albahari "C# 5.0 in a Nutshell: The Definitive Reference"
A BackgroundWorker is a ready to use class in WinForms allowing you to execute tasks on background threads which avoids freezing the UI and in addition to this allows you to easily marshal the execution of the success callback on the main thread which gives you the possibility to update the user interface with the ...
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With