Click here to Skip to main content
15,887,596 members
Articles / Programming Languages / C#
Tip/Trick

Switching From a BackgroundWorker To a Task - It's a Neater Solution, Particularly When Reporting Progress

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
17 Jan 2024CPOL1 min read 10.8K   21   13
Why switch from the "traditional" BackgroundWorker to a Task? The old way works OK, but the new way generates "tidier" code which keeps stuff together (and needs less code)
I've used BackgroundWorkers for a long time - most of my apps use them to free up the UI from long running tasks, and they have a built in progress reporting mechanism which a "raw" Thread lacks. But I've recently realized that the Task class can do it as well, and in a really tidy way. So this tip shows you the old and new ways, so you can easily switch.

Introduction

BackgroundWorker is fine; they work; they report progress to the UI thread via an event; they allow both a "percentage complete" value and a user defined object to be handed up to the progress report handler method. But ... they take work to set up, and aren't strongly typed which raises the possibility of runtime errors that can't be caught at compile time.

Task also provides progress reporting via an event, but this is strongly typed via the IProgress<T> Interface which means you can't pass the wrong type of data to the handler. The code is also a lot cleaner and more obvious.

BackgroundWorker Code

C#
private void MyButton_Click(object sender, EventArgs e)
    {
    showProgress.Show();
    BackgroundWorker work = new BackgroundWorker();
    work.WorkerReportsProgress = true;
    work.ProgressChanged += Work_ProgressChanged;
    work.RunWorkerCompleted += Work_RunWorkerCompleted;
    work.DoWork += Work_DoWork;
    work.RunWorkerAsync();
    }
    
private void Work_DoWork(object sender, DoWorkEventArgs e)
    {
    if (sender is BackgroundWorker work)
        {
        for (int i = 0; i != 100; ++i)
            {
            work.ReportProgress(i);
            Thread.Sleep(100);
            }
        }
    }
    
private void Work_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    showProgress.Hide();
    }
    
private void Work_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
    showProgress.Value = e.ProgressPercentage;
    if (e.UserState is DataTable dt)
        {
        DoSomething(dt);
        }
    }

Yes, you could use inline handlers, but ... no: because of the lack of strong typing, you get clumsy code anyway.

Task Code

C#
private async void MyOtherButton_Click(object sender, EventArgs e)
    {
    showProgress.Show();
    IProgress<int> progress = new Progress<int>(value => showProgress.Value = value);
    await Task.Run(() =>
    {
        for (int i = 0; i != 100; ++i)
            {
            progress.Report(i);
            Thread.Sleep(100);
            }
    });
    showProgress.Hide();
    }

Or:

C#
private async void OrMyOtherButton_Click(object sender, EventArgs e)
    {
    showProgress.Show();
    IProgress<DataTable> progress = new Progress<DataTable>(dt => DoSomething(dt));
    await Task.Run(() =>
    {
        for (int i = 0; i != 100; ++i)
            {
            progress.Report(new DataTable());
            Thread.Sleep(100);
            }
    });
    showProgress.Hide();
    }

Note that your handler method now needs the async keyword.

To me, that's way more readable, and more obvious when you show and hide the progress bar.

Obviously, if you need to pass multiple objects to your progress handler, the usual suspects are available: a home grown class or struct, a KeyValuePair, a tuple... pick your favourite!

And ... Task is more flexible: you can use Task.AwaitAll to wait for a whole bunch of tasks to complete a lot more easily than you can do the same with a BackGroundWorker!

History

  • 17th January, 2024: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO
Wales Wales
Born at an early age, he grew older. At the same time, his hair grew longer, and was tied up behind his head.
Has problems spelling the word "the".
Invented the portable cat-flap.
Currently, has not died yet. Or has he?

Comments and Discussions

 
QuestionTask & Async Await - In my opinion just more Syntactic Sugar. Pin
Russell Mangel8-Apr-24 2:38
Russell Mangel8-Apr-24 2:38 
AnswerRe: Task & Async Await - In my opinion just more Syntactic Sugar. Pin
OriginalGriff8-Apr-24 4:05
mveOriginalGriff8-Apr-24 4:05 
GeneralRe: Task & Async Await - In my opinion just more Syntactic Sugar. Pin
Russell Mangel8-Apr-24 4:54
Russell Mangel8-Apr-24 4:54 
GeneralRe: Task & Async Await - In my opinion just more Syntactic Sugar. Pin
Russell Mangel8-Apr-24 5:09
Russell Mangel8-Apr-24 5:09 
PraiseGreat little article Pin
Bruce Greene19-Jan-24 11:09
Bruce Greene19-Jan-24 11:09 
GeneralRe: Great little article Pin
OriginalGriff19-Jan-24 23:24
mveOriginalGriff19-Jan-24 23:24 
PraisePortable cat-flap Pin
Terry Hutt18-Jan-24 11:43
Terry Hutt18-Jan-24 11:43 
GeneralRe: Portable cat-flap Pin
OriginalGriff18-Jan-24 19:33
mveOriginalGriff18-Jan-24 19:33 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA18-Jan-24 3:56
professionalȘtefan-Mihai MOGA18-Jan-24 3:56 
QuestionSome notes Pin
Graeme_Grant17-Jan-24 6:32
mvaGraeme_Grant17-Jan-24 6:32 
AnswerRe: Some notes Pin
OriginalGriff17-Jan-24 11:44
mveOriginalGriff17-Jan-24 11:44 
GeneralRe: Some notes Pin
Graeme_Grant17-Jan-24 12:05
mvaGraeme_Grant17-Jan-24 12:05 
QuestionGot my 5 Pin
Mike Hankey17-Jan-24 4:01
mveMike Hankey17-Jan-24 4:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.