Monday 3 April 2017

Background worker

In .net 2.0 microsoft introduced the BackgroundWorker class, it was the first step to abstracting away the creation of threads and the use of the threadpool class. When your application uploads/downloads data or saves to storage the UI can appear unresponsive; alleviate these symptoms the BackgroundWorker class is a great candidate.

using System;
using System.ComponentModel;
using System.Threading;

namespace pc.BackgroundworkerExmaple
{
    class Program
    {
        static void Main(string[] args)
        {
            char selection;
            bool showProgress = false;

            var bgw = new BackgroundWorker();
            bgw.WorkerReportsProgress = true;
            bgw.WorkerSupportsCancellation = true;

            bgw.DoWork += (s, e) =>
            {
                var worker = s as BackgroundWorker;

                for (int i = 0; i < 10; i++)
                    if (worker.CancellationPending)
                    {
                        e.Cancel = true;
                        break;
                    }
                    else
                    {
                        Thread.Sleep(1000);
                        worker.ReportProgress(i * 10);
                    }
            };

            bgw.RunWorkerCompleted += (s, e) =>
            {
                var worker = s as BackgroundWorker;

                if (e.Cancelled)
                    Console.WriteLine("Canceled");
                else if (e.Error != null)
                    Console.WriteLine($"error: {e.Error.Message}");
                else
                    Console.WriteLine("Background task complete");
            };

            bgw.ProgressChanged += (s, e) => {
                if(showProgress)
                    Console.WriteLine($"{e.ProgressPercentage}%");
                showProgress = false;
            };

            do
            {
                if (bgw.IsBusy)
                    Console.Write("2) Progress\n3) Cancel");
                else
                    Console.Write("1) Start\n");
                Console.WriteLine("0) Exit");
                selection = Console.ReadKey().KeyChar;

                switch (selection)
                {
                    case '1':
                        if (!bgw.IsBusy)
                            bgw.RunWorkerAsync();
                        else
                            Console.WriteLine("already running");
                        break;
                    case '2':
                        if (bgw.IsBusy)
                            showProgress = true;
                        else
                            Console.WriteLine("not running");
                        break;
                    case '3':
                        if(bgw.IsBusy)
                        bgw.CancelAsync();
                        else
                            Console.WriteLine("not running");
                        break;
                }
            } while (selection != '0');
        }
    }
}


now this class handles a lot of our standard needs when it comes to background threads, reporting progress, cancelling, a completion callback, it doesn't really handle pausing, but then again it's an old class and you probably wont see it unless you're supporting legacy applications. as long as it's your UI thread that starts the background worker by calling the "RunWorkerAsync" method (which can take an object parameter) then it's guaranteed that the RunWorkerCompleted event handler fires on the UI thread.