58

I am writing a windows application that runs a sequence of digital IO actions repeatedly.

This sequence of actions starts when the user click a "START" button, and it is done by a background worker in backgroundWorker1_DoWork().

However, there are occasions when I get the "This backgroundworker is currently busy......." error message.

I am thinking of implementing the following in the code, by using a while loop to "kill" the background worker before starting another sequence of action:

if (backgroundWorker1.IsBusy == true)
{

    backgroundWorker1.CancelAsync();
    while (backgroundWorker1.IsBusy == true)
    {
        backgroundWorker1.CancelAsync();
    }

    backgroundWorker1.Dispose();

}

backgroundWorker1.RunWorkerAsync();

I think my main concern is, will the backgroundWorker1 be "killed" eventually? If it will, will it take a long time to complete it?

Will this coding get me into an infinite loop?

lc.
  • 113,939
  • 20
  • 158
  • 187
  • 2
    Why do you need a loop? Wouldn't killing it once mean it is no longer busy? – lsl Apr 29 '09 at 03:41
  • Further, it would be wise to put a sleep in there that is appropriate to the application. Also, what happens if the worker is legitimately busy? Would you still want it killing it in the middle of a process? I think it might be wiser to stop what ever is forcing the background worker from becoming indefinitely busy. – lsl Apr 29 '09 at 03:44

6 Answers6

80

You can use something like this (for more information about aborting managed threads and about ThreadAbortException see "Plumbing the Depths of the ThreadAbortException Using Rotor (Web archive)" by Chris Sells):

public class AbortableBackgroundWorker : BackgroundWorker
{

    private Thread workerThread;

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        workerThread = Thread.CurrentThread;
        try
        {
            base.OnDoWork(e);
        }
        catch (ThreadAbortException)
        {
            e.Cancel = true; //We must set Cancel property to true!
            Thread.ResetAbort(); //Prevents ThreadAbortException propagation
        }
    }

 
    public void Abort()
    {
        if (workerThread != null)
        {
            workerThread.Abort();
            workerThread = null;
        }
    }
}

Usage:

backgroundWorker1 = new AbortableBackgroundWorker();
//...
backgroundWorker1.RunWorkerAsync();

if (backgroundWorker1.IsBusy == true)
{
    backgroundWorker1.Abort();
    backgroundWorker1.Dispose();
}
MHN
  • 103
  • 2
  • 7
Sergey Teplyakov
  • 11,477
  • 34
  • 49
  • I like this a lot. It really seems to do the job. I've got some complicated things going on in the BackgroundWorker, but nothing is getting written until it completes. – Tom Anderson Aug 22 '11 at 11:14
  • 4
    BTW, it's still dangerous to kill thread with Thread.Abort, because not every class in .net framework robust in terms of asynchronous exceptions. But if you know what are you doing - its ok. – Sergey Teplyakov Aug 22 '11 at 13:35
  • I was very happy to find this code at first, but when I tried this It took ages for the thread to Abort... I was not expecting such a behaviour and I am a bit confused. Really have no Idea why this takes so long – Mehdi LAMRANI Jun 28 '12 at 17:22
  • 3
    There are several tips: Thread.Abort can't abort the thread when thread executes: 1) finally blocks, 2) critical execution regions, 3) unmanaged code. This means that if your code executes third party code or some other code, you can't predict when this thread would be aborted. Please take a look at Eric's post: http://blogs.msdn.com/b/ericlippert/archive/2010/02/22/should-i-specify-a-timeout.aspx – Sergey Teplyakov Jun 29 '12 at 20:22
  • @SergeyTeplyakov if i am running this AbortableBackgroundWorker from a UI, doesn't the current thread is the UI here? {workerThread = Thread.CurrentThread;} Aborting the current thread isn't somehow wrong? – Emi Dec 27 '12 at 05:02
  • This is awesome! great job :) – Ehsan Sajjad Apr 13 '16 at 15:11
  • Caution: this code contains race condition. You cannot abort thread before it executed line `workerThread = Thread.CurrentThread;` – Pavel Mayorov Mar 28 '17 at 07:52
  • Hello, After long hard time with CancelAsync(), finally I'm using your code. Can you kindly help me to solve a deadlock? https://stackoverflow.com/questions/47952915/when-i-abort-backgroundworkers-too-quickly-they-becomes-always-busy – Kay Lee Dec 23 '17 at 13:57
  • I'm turning the Microsoft example for TCPListener command-line app into a background worker. DoWork() sits on the blocking call TcpClient client = _server.AcceptTcpClient(); The call to Abort does not knock the abortablebackgroundworker out of its DoWork() under .NET 3.5 framework. I guess I'll wade into the quagmire of race conditions from doing it proper async with BeginAcceptTCPClient. Race conditions massively expand the testing burden. Dang. – user922020 Jun 05 '18 at 17:16
20

I'm of the opinion that threads should be responsible for their own resources as much as practicable, including their own lifetime.

It's usually a bad idea to kill threads from outside their scope. Applications that are engineered to pass a message to the thread to shut itself down tend to have far less problems related to multi-threaded behavior.

A thread should monitor for said message, which can be as simple as a boolean set by another thread and read by that monitoring thread, in a timely fashion and shut itself down cleanly as soon as it can.

That means if it should look for the message:

  • in it's main loop, if any.
  • periodically in any long-running loops.

The thread shutting it down with the message should wait (but don't halt the GUI, of course).

Note that there are other possibilities for threaded environments with specific capabilities such as the case where threads can mark themselves cancellable at will, to allow external killing to be safer.

But it's still usually easier to just architect your application to leave a thread master of its own destiny.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

I was having the same problem, I'm not sure if this will help but I'm guessing your background worker has a loop within or it would exit. What you need to do is put your loop within.

Put inside your background worker:

while (backgroundworker1.CancellationPending == false)
{
    //Put your code in here
}

To kill this backgroundworker, you can put in your button:

BackgroundWorker1.CancelAsync()

I hope this helps.

Carol
  • 1,852
  • 26
  • 29
echrom
  • 67
  • 1
0

I put one together that (i think) does the job. Please let me know if im waaaay off. Here is a simple exaple of how it works.

var backgroundWorker = new BackgroundWorker(){WorkerSupportsCancellation = true};

backgroundWorker.DoWork += (sender, args) =>
         {                 
                 var thisWorker = sender as BackgroundWorker;
                 var _child = new Thread(() =>
                                               {
                                                   //..Do Some Code

                                               });
                 _child .Start();
                 while (_child.IsAlive)
                 {
                     if (thisWorker.CancellationPending)
                     {
                         _child.Abort();
                         args.Cancel = true;
                     }
                     Thread.SpinWait(1);
                 }                 
         };

 backgroundWorker.RunWorkerAsync(parameter);
 //..Do Something...
backgroundWorker.CancelAsync();

Since the background worker is part of the thread pool, we dont want to abort it. But we can run a thread internally which we can allow an abort to occur on. The backgroundWorker then basically runs until either the child thread is complete or we signal to it to kill the process. The background worker thread can then go back into the read pool. Typically I will wrap this up in a helper class and pass through the delegate method that I want the background thread to run passed in as the parameter and run that in the child thread.

Please someone let me know if im banging my head against a wall but it seems to work fine.. But thats the problem with threads isnt it.. the varying results you can get when you run it at different times.

Rob
  • 1,663
  • 1
  • 19
  • 16
  • 2
    Very, very dangerous to `Abort` a thread externally. Please read: http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation. Basically, this is very very wrong in **three** ways: (1) creating an unnecessary extra thread, (2) busy-waiting on it, and (3) aborting it at a potentially critical moment without allowing proper cleanup. – Aaronaught Apr 07 '10 at 03:15
  • The response above also uses a child-thread + abort type of solution. Is there a better way to deal with this? – Rob Apr 07 '10 at 06:06
0

i just use a 'return' to go to end of thread:

private void myWorker_DoWork(object sender, DoWorkEventArgs e)
{
   // some variables 

   while(true)
   {
      // do something ...
      if(need_cancel) return;
      else doSomeThingElse();
   }

   // may be more code here (ignored if 'return' is executed) ...
   // .........
   // .........
}
Danny85
  • 1
  • 7
-1
public class abortableBackgroundworker: BackgroundWorker
{
    public bool Kill = false;

    protected override void OnDoWork(DoWorkEventArgs e)
    {


        var _child = new Thread(() =>
        {
            while (!Kill)
            {

            }
            e.Cancel = true;//..Do Some Code

        });
        _child.Start();
        base.OnDoWork(e);

    }



}

you set kill to true to kill the thread and no abort problem :)

axel22
  • 32,045
  • 9
  • 125
  • 137
Daniel
  • 1