views:

831

answers:

4
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ClassLibrary
{
    public class MyClass
    {
        public static string LongOperation()
        {
            Thread.Sleep(new TimeSpan(0,0,30));

            return "HelloWorld";
        }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using ClassLibrary;

namespace BackgroungWorker__HelloWorld
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            if (backgroundWorker1.CancellationPending)
            {
                e.Cancel = true;
                return;
            }

            MyClass.LongOperation();

            e.Result = "[Result]";
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {            
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                if (e.Cancelled)
                {
                    MessageBox.Show("The task has been cancelled");
                }
                else if (e.Error != null)
                {
                    MessageBox.Show("Error. Details: " + (e.Error as Exception).ToString());
                }
                else
                {
                    MessageBox.Show("The task has been completed. Results: " + e.Result!=null?e.Result.ToString():" null");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy)
            {
                backgroundWorker1.CancelAsync();
            }
            else
            {
                this.Close();
            }
        }
    }
}

I found that, after executing the following code :

private void btnClose_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy)
            {
                backgroundWorker1.CancelAsync();
            }
            else
            {
                this.Close();//"this" means the Form object
            }
        }

the backgroundWorker1's thread is not killed immediately. It takes some time.

This is been a problem in constructing my application logic.

Can anyone help me in this regard?

+1  A: 

This is dependent on how frequently your DoWork() method checks the CancellationPending-property.

It is a request of cancellation, you are not killing the thread. Refer to the msdn page on CancelAsync

There is no real way to forcefully terminate the BackgroundWorker, use a Thread instead.

Yannick M.
+4  A: 

The solution is to change your BackgroundThread's DoWork() event handler to be more responsive: it needs to break its work into smaller chunks and poll the worker's CancellationPending frequently enough to satisfy your application's needs.


Edit: given the code you've added, you won't be able to do what you'd like with BackgroudWorker.

If you cannot modify MyClass.LongOperation (and it doesn't provide any hooks to let you interrupt it) but you want to let the user cancel before that operation finishes, you could implement this by creating your own Thread (as a background thread, which won't keep your application open if you abandon it). Theoretically, you could also do it using ThreadPool.QueueUserWorkItem, except that it's a bad idea to use the ThreadPool for long-running processes (see The Managed Thread Pool for details).

Finally, you could consider moving the long-running operation out of band - write a message to a queue, call a service, or use some other technique to hand it off to another process.

Jeff Sternal
+1. Dang, 4 identical answers all within 1 minute. I just deleted mine.
Steve Wortham
This is not possible, if I need to keep my lower logic layers untouched. Any other way?
JMSA
@JMSA - not as far as I know. There are several similar questions on SO (for example, http://stackoverflow.com/questions/543811/kill-the-thread-of-backgroundworker). The upshot is that you have to use a different mechanism than BackgroundWorker if you plan to abandon your threads.
Jeff Sternal
+1  A: 

It's up to the workerthreads code to check the CancellationPending property. The code that is executed in between these checks will always be executed, causing a delay.

In your remark you git a nice isssue.

One way to solve this is building a specific subclass for multi threading, to leave the unavoidable nasty bits out of the logic.

The main class should provide a template method for LongOperation that calls to other methods, the threading subclass can then override the methods called in longoperation, and before letting the mainclass methods do the actual work perform the check of the CancellationPending property.

This way you can stop more arbitrary than at the end of longoperation.

Multithreading in a nonfunctional way will always affect your code, hold on for when you will be needing locks ;-)

Glenner003
A: 

Can you describe what the issue is? Surely it is better to let the background worker thread gracefully finish (as it does by design).

You are aware that you can periodically check for a cancellation by doing backgroundWorker1.CancellationPending?

RichardOD