Home > .Net Framework, C#, Performance, WPF > Cancel BackgroundWorker threads when closing windows / applications

Cancel BackgroundWorker threads when closing windows / applications

The System.ComponentModel.BackgroundWorker class is useful when doing large amounts of work while still keeping the UI responsive.  They do however need to be cleaned up manually if they are not complete when windows / forms are closed. 

The example below shows one way to implement a clean up system to ensure all workers are cancelled and disposed of properly when closing objects. 

The code below is a normal Background Worker Method that will never complete.  While this is not normal – this demonstrates the point that the background thread keeps running even when the UI window is closed.  The window is closed – but the object is not disposed (garbage collected) since a reference between the window and the worker still exists. 

Download Source Code here: WPFBackGroundWorkerDemo.zip

private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        }

 void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bw = sender as BackgroundWorker;
            bool isRunning = true;
            double counter = 0;
            while(isRunning)
            {
                bw.ReportProgress(0, "Work is being done.... " + counter);
                counter++;
                Thread.Sleep(5000);
            }
        }

If we run this and put a break point on the Thread.Sleep(5000) section and look at the threads window we can see our background thread running.

image

Now we close the SecondWindow and look at the threads again (put break point after the sw.ShowDialog(); in the Main Window class.  You see the arrow shows we are now back on the main thread but the worker thread is still running.  If you put a break point inside the while loop – it will stop at the break point every 5 seconds even though the second window is no longer in view.

This poses issues with performance, cleanup, etc.  These items should be cleaned up when closing the parent window (note there are always some exceptions to this for calculation operations, etc).

1.  Add a List as a class variable

private List<BackgroundWorker> _workers = new List<BackgroundWorker>();

2.  Each time a background worker is created – add it to the list

 private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            BackgroundWorker bw = new BackgroundWorker();
            // Add the worker to the list of workers
            _workers.Add(bw);
            bw.WorkerSupportsCancellation = true;
            bw.WorkerReportsProgress=true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();
        }

3.  In the worker completed event – remove it from the list

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Once we are finished with the work - remove it from the list
            BackgroundWorker bw = sender as BackgroundWorker;
            if(bw!= null && _workers.Contains(bw))
            {
                _workers.Remove(bw);
            }
        }

4.  At this point – the worker still performs the same – the next step is where the cancellation comes into play.  I added a method to check the worker list and cancel any workers that are in the list.  This is called before the form / window is closed. 

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // If the list is not null and has workers
            if(_workers != null && _workers.Count > 0)
            {
                // Loop through the list
                foreach (BackgroundWorker bw in _workers)
                {
                    // If the worker is busy - cancel it
                    if(bw.IsBusy)
                    {
                        bw.CancelAsync();
                    }
                }
            }
        }

5.  You must also add logic to the worker dowork() method to return if cancel selected. 

 while(isRunning)
            {
                if (bw.CancellationPending)
                    return;
                bw.ReportProgress(0, "Work is being done.... " + counter);

 

Now for the test. 

image

If you close the SecondWindow killing the background worker – the text box on the MainWindow will not update.  If you close without killing – the text box will keep updating. 

 

Download Source Code here: WPFBackGroundWorkerDemo.zip

About these ads
  1. Debasish
    December 13, 2011 at 11:49 pm | #1

    I used exact way to cancel the BackgroundWorker before closing Window, immediately my window get closed, but it take much time to cancel the BackgroundWorker and as a result it gives Object reference not set to an instance of an object. exception.
    Can u please give me some alternate solutions

  1. No trackbacks yet.
You must be logged in to post a comment.
Follow

Get every new post delivered to your Inbox.

Join 166 other followers

%d bloggers like this: