Task Parallel Library Replacement for Backgroundworker

Task parallel library replacement for BackgroundWorker?

The Task class is an improvement over the BackgroundWorker; it naturally supports nesting (parent/child tasks), uses the new cancellation API, task continuations, etc.

I have an example on my blog, showing the old BackgroundWorker way of doing things and the new Task way of doing things. I do have a small helper class for tasks that need to report progress, because I find the syntax rather awkward. The example covers result values, error conditions, cancellation, and progress reporting.

Replacing methods that use backgroundworker to async / tpl (.NET 4.0)

You should just use the Task Parallel Library (TPL). The key is specifying the TaskScheduler for the current SynchronizationContext for any continuations in which you update the UI. For example:

Task.Factory.StartNew(() =>
{
return ProcessMethod(yourArgument);
})
.ContinueWith(antecedent =>
{
UpdateView(antecedent.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());

Aside from some exception handling when accessing the antecedent's Result property, that's all there is too it. By using FromCurrentSynchronizationContext() the ambient SynchronizationContext that comes from WPF (i.e. the DispatcherSynchronizationContext) will be used to execute the continuation. This is the same as calling Dispatcher.[Begin]Invoke, but you are completely abstracted from it.

If you wanted to get even "cleaner", if you control ProcessMethod I would actually rewrite that to return a Task and let it own how that gets spun up (can still use StartNew internally). That way you abstract the caller from the async execution decisions that ProcessMethod might want to make on its own and instead they only have to worry about chaining on a continuation to wait for the result.

UPDATE 5/22/2013

It should be noted that with the advent of .NET 4.5 and the async language support in C# this prescribed technique is outdated and you can simply rely on those features to execute a specific task using await Task.Run and then execution after that will take place on the Dispatcher thread again automagically. So something like this:

MyResultType processingResult = await Task.Run(() =>
{
return ProcessMethod(yourArgument);
});

UpdateView(processingResult);

C# BackgroundWorker with Library

My suggestion is to ditch the anachronistic BackgroundWorker class, and use Task.Run and async/await instead. First create an asynchronous method that will loop continuously every 250 msec and will update the labels:

private async Task InfiniteLoopAsync(CancellationToken cancellationToken = default)
{
while (true)
{
var delayTask = Task.Delay(250, cancellationToken);
var value10 = await Task.Run(() => modbusclient.ReadInputRegisters(4, 1)[0]);
var value11 = await Task.Run(() => modbusclient.ReadInputRegisters(5, 1)[0]);
var value12 = await Task.Run(() => modbusclient.ReadInputRegisters(6, 1)[0]);
var value14 = await Task.Run(() => modbusclient.ReadInputRegisters(8, 1)[0]);
label10.Text = value10.ToString() + " kHz";
label11.Text = value11.ToString() + " W";
label12.Text = value12.ToString() + " %";
TimeSpan t = TimeSpan.FromMilliseconds(Convert.ToDouble(value14));
label14.Text = $"{t.Minutes:D2}m:{t.Seconds:D2}s";
await delayTask;
}
}

Then start the asynchronous loop when the form is first shown, and eventually stop the loop when the form is about to close:

private CancellationTokenSource _cts = new();
private Task _infiniteLoop = Task.CompletedTask;

private void Form_Shown(object sender, EventArgs e)
{
_infiniteLoopTask = InfiniteLoopAsync(_cts.Token);
}

private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
_cts.Cancel();
// Wait the completion of the loop before closing the form
try { _infiniteLoopTask.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { } // Ignore this error
}

It's important that any interaction with the UI controls happens exclusively on the UI thread. You can only offload code to the ThreadPool only if this code neither reads or updates UI components. With the await Task.Run you are handed the result of the offloaded operation, and you are back on the UI thread where it's legal to update the labels.

Getting away from BackgroundWorker towards TPL for a logging class

Right now your code isn't thread-safe as you have this:

public List<LogEvent> Events { get; private set; }

List<T> isn't thread safe, and can be changed from outer code. And I can't see is it used at all.

Also, you really should use CancellationToken in your code because of in other case you can get troubles: for example, you have 5 messages in queue, and you decide to cancel your work. In this case check for Shutdown Log will break loop only after some time, which is confusing for end-user of your class.

Also, there is an overload for BlockingCollection<T>.Take Method with CancellationToken, but in case of cancel you'll get to catch the OperationCanceledException:

try
{
LogEvent anEvent = WriteQueue.Take(CancellationPending);
}
catch (OperationCanceledException ex)
{
// handle stop here;
}

Infinite loop in very bad practice in multi-threading, I suggest not to use it.

BackgroundWorker or Task, which to implement?

BackgroundWorker has been superseded by Tasks and in general you should use the latter when possible.

Note that besides Tasks having more features, there are also subtle differences which may be relevant. For instance, a Task may not necessarily be run on a different thread, while BackgroundWorker.DoWork does.

Arguably, BackgroundWorker is easier to use and you will still need it when developing in .NET versions prior to 4.

I wouldn't recommend refactoring your existing code, just start using Tasks in your new base. I also find Tasks easier to integrate with UIs but that may be subjective.

Threading, BackgroundWorker or Task, numerous objects processed by the same method

You're free to use whichever tool you want to accomplish the task. They two are not interchangeable, in that they have somewhat different (although overlapping) design goals. Which you prefer is something that we can't answer.

Whether you want all of the workers to execute a method of the same instance of an object or a different instance is entirely dependent on context. Neither is inherently wrong, so long as you understand whether or not the various threads are acting on the same instance or not, and write the code accordingly. Both, in the right circumstances, can be entirely acceptable. It all comes down to whether you want the various threads to share state or not, and whether that given state is specifically designed to be accessed from multiple threads.

Async/await vs BackgroundWorker

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?



Related Topics



Leave a reply



Submit