In this post we’re going to see 2 different implementations for executing tasks in a background thread. This is especially useful for keeping the user interface responsive and the user delighted at all times.

The app

I created a small WPF app that starts a background thread just to try out 2 different approaches that both report progress - we don’t want our users left in the dark in regards to how long the operation will take, do we?

  • async tasks
  • BackgroundWorker

Here’s how the app looks like:

Async Task vs BackgroundWorker app

Async task

private async void AsyncTaskButton_Click(object sender, RoutedEventArgs e)
{
    IProgress<int> progress = new Progress<int>(percentCompleted =>
    {
        AsyncTaskProgressBar.Value = percentCompleted;
    });

    AsyncTaskButton.IsEnabled = false;
    await Task.Run(async () =>
    {
        progress.Report(0);
        foreach (var i in Enumerable.Range(1, 4))
        {
            await Task.Delay(1000);
            progress.Report(i * 25);
        }
    });
    AsyncTaskButton.IsEnabled = true;
}

Pretty short and simple, huh?

BackgroundWorker

private void BackgroundWorkerButton_Click(object sender, RoutedEventArgs e)
{
    BackgroundWorkerButton.IsEnabled = false;
    var backgroundWorker = new BackgroundWorker()
    {
        WorkerReportsProgress = true
    };

    backgroundWorker.DoWork += (s, args) =>
    {
        backgroundWorker.ReportProgress(0);
        foreach (var i in Enumerable.Range(1, 4))
        {
            Thread.Sleep(1000);
            backgroundWorker.ReportProgress(i * 25);
        }
    };

    backgroundWorker.ProgressChanged += (s, args) =>
    {
        BackgroundWorkerProgressBar.Value = args.ProgressPercentage;
    };

    backgroundWorker.RunWorkerCompleted += (s, args) =>
    {
        BackgroundWorkerButton.IsEnabled = true;
    };

    backgroundWorker.RunWorkerAsync();
}

This approach is slightly longer, but it works the same way.

What Microsoft recommends

Here’s what Microsoft has to say about which approach to use:

The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than the BackgroundWorker class for I/O-bound operations because the code is simpler and you don’t have to guard against race conditions. In combination with the Task.Run method, 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.

More examples from Stephen Cleary

Stephen Cleary is the Concurrency in C# Cookbook author and he also wrote a series of blog posts where he details the differences between async and BackgroundWorker from a few different perspectives: error handling, returning results, cancellation and reporting progress. He arrives at the same conclusion: async is better than BackgroundWorker:

I hope that this series is sufficient to convince you that BackgroundWorker is a type that should not be used in new code. Everything it can do, Task.Run can do better; and Task.Run can do a lot of things that BackgroundWorker can’t!

Without further ado, I present you the first post in the series. Be sure to check ALL of them: Stephen Cleary: Task.Run vs BackgroundWorker.

Source Code

The full source code is available on GitHub. You can clone the repo and try it locally.

Resources