Hi, I'm Ray

I'm a software developer, full-time nerd and occasional human (while heavily caffeinated).

C#: Quick and easy multi-threading on Windows Forms App with Tasks (Async/Await)

A couple of months ago I posted about multi-threading using the BackgroundWorker. This time I’m going to show multi-threading using Tasks, also known as async/await. The two patterns are practically identical and I’ve kept the structure of the code the same to highlight the comparison.

So, what’s the difference?

In my mind, in practical terms, the BackgroundWorker is part of the System.Windows.Forms namespace and is used for threading the user interface (Forms). If you need a progress bar updated while work is being done, the BackgroundWorker is ideal.

Tasks can be used anywhere, including Forms and Web applications.

Can I use Tasks for everything and forget about the BackgroundWorker?

Yes. The answer is, “yes”.

 

Let’s get started.

 

I’m using the same example from the other post. There is a randomly generated number every half a second (or so). The button1 starts and stops the thread. The running thread updates label1 with a slightly randy number.

example-randy-form

 

1. Setup

We’re adding a couple of variables to help us monitor the state of the task. We’re following the same code structure as the previous post but this would normally be simplified.


using System.Threading;
using System.Threading.Tasks;

protected bool isBusy = false;
protected bool cancelPending = false;

private async Task doWork()
{
}

private void runWorkerCompleted()
{
}

 

2. Start/Stop Button


private async void button1_Click(object sender, EventArgs e)
{
  if (isBusy)
  {
    cancelPending = true;
    return;
  }

  isBusy = true;

  await doWork();

  runWorkerCompleted();
}

 

3. InvokeRequired

We still have to avoid illegal cross-threading (no changes here).


protected void setLabelText(string text)
{
  if (label1.InvokeRequired)
  {
    label1.Invoke(new MethodInvoker(() => {
      label1.Text = text;
    }));
  }
  else
  {
    label1.Text = text;
  }
}

 

4. The Whole Things

This is the whole file bringing everything together.


  public partial class Form1 : Form
  {
    protected bool isBusy = false;
    protected bool cancelPending = false;

    protected Random randy = null;

    public Form1()
    {
      InitializeComponent();

      randy = new Random();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
      if (isBusy)
      {
        cancelPending = true;
        return;
      }

      isBusy = true;

      await doWork();

      runWorkerCompleted();
    }

    private async Task doWork()
    {
      await Task.Run(() =>
      {
        while (true)
        {
          if (cancelPending)
          {
            cancelPending = false;
            break;
          }

          setLabelText(randy.Next().ToString());

          Thread.Sleep(500);
        }

      });
    }

    private void runWorkerCompleted()
    {
      cancelPending = false;
      isBusy = false;
    }

    protected void setLabelText(string text)
    {
      if (label1.InvokeRequired)
      {
        label1.Invoke(new MethodInvoker(() => {
          label1.Text = text;
        }));
      }
      else
      {
        label1.Text = text;
      }
    }
  }

 

I hope someone finds this useful.

The Author

Ray
  • Hi, I'm Ray. I'm a software developer, full-time nerd and occasional human (while heavily caffeinated).

Copyright © 2014-2019 Ray Lam. All Rights Reserved.