6

Let's say I have a background worker like this:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
             while(true)
             {
                  //Kill zombies
             }
        }

How can I make this background worker start and stop using a button on a WinForm?

sooprise
  • 22,657
  • 67
  • 188
  • 276
  • 3
    Hmm, for the answer you accepted, beware - you are worrying all about an effect that will only cause it not to stop an iteration or two later, but forgetting you are going to be BUSYING UP a whole processor just spinning! At least put a sleep in there. It will look like it's working but to great detriment. Some of the other answers solve all those problems they way you are 'supposed' to solve them. – FastAl Jun 18 '10 at 15:53

5 Answers5

17

Maybe you can use a manualresetevent like this, I didn't debug this but worth a shot. If it works you won't be having the thread spin its wheels while it's waiting

ManualResetEvent run = new ManualResetEvent(true);

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
     while(run.WaitOne()) 
     { 
         //Kill zombies 
     } 
} 

private void War() 
{ 
    run.Set();
} 

private void Peace() 
{ 
    run.Reset();
}
Steve Sheldon
  • 6,421
  • 3
  • 31
  • 35
  • +1 Its great to see another soul that really understands .NET threading. ;) I think the use of a WaitHandle is always the better choice in these situations, as they intrinsically take care of atomicity and other multithreading issues for you (assuming you choose the right kind of WaitHandle). – jrista Jun 18 '10 at 05:18
  • 1
    `while (true) { if (run.WaitOne()) { } }`? Seems a little redundant, doesn't it? Why not `while (run.WaitOne()) { }`? – Dan Tao Jun 18 '10 at 05:22
  • It is indeed, I thought that myself after posting, i'll update it – Steve Sheldon Jun 18 '10 at 05:26
8

Use the CancelAsync method.

backgroundworker1.CancelAsync();

In your loop inside the worker thread.

if (backgroundWorker.CancellationPending) return;

This doesn't happen immediately.

Aaron Smith
  • 3,332
  • 4
  • 29
  • 25
  • 6
    +1: I don't get why everybody's saying to check `while (_someCustomBoolean)` when the `BackgroundWorker` class already has this functionality built in. – Dan Tao Jun 18 '10 at 05:19
  • 1
    Once cancellation pending is set, I think the general protocol is for the background worker to stop and complete. In this situation I believe the questions was asking for a way to start and stop. The background worker can trip you up as well. If you use its oncompleted event, this gets posted through the .net generic ThreadPool, I have seen this deadlock before depending on what else gets thrown on there. – Steve Sheldon Mar 12 '12 at 19:56
5

This is how to do it (link to answer below)

Community
  • 1
  • 1
Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • "Accessing and updating a bool is an atomic operation". Is this because of the memory barrier? – Josh Smeaton Jun 17 '10 at 21:43
  • No, atomic means that the value true or false will be written in one go. A memory barrier dictates the order that writes or reads of the value are seen by another thread. There's nothing which says that the value written to _performKilling will ever be visible in another thread without such a mechanism. Atomicity means that the value read will always either be true or false, it does not mean that the background thread will ever see the most recently written value. – Pete Kirkham Jun 17 '10 at 21:48
  • 1
    They are atomic but to be on the safe side I would likely mark it volatile. This tight loop doesn't take breather either. It's constantly building data as fast as it can go, I wouldn't post any invalidate calls from this loop for instance. – Steve Sheldon Jun 17 '10 at 21:51
  • Definitely mark the flag volatile otherwise you might see heisenbugs. – spender Jun 17 '10 at 22:53
  • Yes, I should have clarified that by *thread safety* I was referring to the fact that the value will be written and read "in one go". Marking the flag `volatile` might steer up the order of access a bit (but not completely), so to be really on the safe side when it comes to order of access and freshness of data, some sort of locking mechanism should probably be introduced (I'll update the answer with that). – Fredrik Mörk Jun 18 '10 at 05:08
  • This is a _horrible_ way to do this. As soon as you want to "pause" the worker, it goes into a non-waiting busy loop, which will send CPU utilization skyrocketing with nothing to show for it. (A full `lock` is overkill -- `volatile` would be sufficient -- but fixing that is putting lipstick on a pig.) See a **much better** approach in [this answer](http://stackoverflow.com/a/3065784) by Steve Sheldon. – Peter Duniho Apr 08 '17 at 06:34
  • Yes, @PeterDuniho, Steve's answer is way better, and certainly how I would do it today (that or use a CancellationToken). Looking back at this code sample I start to wonder what I was thinking. Should probably delete this one :-) – Fredrik Mörk Apr 08 '17 at 10:39
  • _"probably delete this one"_ -- I don't believe you'll be able to do that, as long as your answer is the "accepted" answer. The question author hasn't been around for a long time, so getting them to un-accept might be difficult. – Peter Duniho Apr 08 '17 at 17:50
  • Your other option would be to edit the answer so that at least it isn't providing such bad advice. You could refer to the better answer already posted, or do a little more work and show how the worker state could be stored in an object, so that the worker can literally be interrupted and resumed later (that would be a novel answer relative to what's here, make it a useful addition to the existing answers). – Peter Duniho Apr 08 '17 at 17:50
4

By stop do you really mean stop or do you mean pause? If you mean stop, then this is a piece of cake. Create a button click event handler for the button you want to be responsible for starting the background worker and a button click event handler for the one responsible for stopping it. On your start button, make a call to the background worker method that fires the do_work event. Something like this:

private void startButton_Click(System.Object sender, 
    System.EventArgs e)
{
    // Start the asynchronous operation.
    backgroundWorker1.RunWorkerAsync();
}

On your stop button, make a call to the method that sets the background worker's CancellationPending to true, like this:

private void cancelAsyncButton_Click(System.Object sender, 
    System.EventArgs e)
{   
    // Cancel the asynchronous operation.
    this.backgroundWorker1.CancelAsync();
}

Now don't forget to check for the CancelationPending flag inside your background worker's doWork. Something like this:

   private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e)
   {
        while (true)
        {
            if (worker.CancellationPending)
           {   
              e.Cancel = true;
           }
        }
   }

And your doWork method:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
             BackgroundWorker worker = sender as BackgroundWorker;
             KillZombies(worker, e);
        }

I hope this can steer you in the right direction. Some further readings:

http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.90).aspx

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

http://msdn.microsoft.com/en-us/library/waw3xexc.aspx

vitorbal
  • 2,871
  • 24
  • 24
0

I haven't tested this, I have code somewhere that I'll have to see exactly what I did, but something like this is an adaptation of Fredrik's answer:

private bool _performKilling;
private object _lockObject = new object();

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
     while(true)
     {

         if (_performKilling)
         {
             //Kill zombies
         }
         else
         { //We pause until we are woken up so as not to consume cycles
             Monitor.Wait(_lockObject);
         }
     }
}

private void StartKilling()
{
    _performKilling = true;
    Monitor.Pulse(_lockObject);
}

private void StopAllThatKilling()
{
    _performKilling = false;
]

More complete example of this pattern here:

https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs

AaronLS
  • 37,329
  • 20
  • 143
  • 202
  • Ah, of course; nice solution. I must be tired not coming up with it from the start myself ;) – Fredrik Mörk Jun 17 '10 at 21:30
  • _"I haven't tested this"_ -- clearly, since the calls to `Wait()` and `Pulse()` will throw exceptions, because you haven't taken the monitor before calling those methods. – Peter Duniho Apr 08 '17 at 06:37
  • @PeterDuniho Of course there's some plumbing not shown here. Was just meant to should the couple of methods one uses on a Monitor to facilitate pausing/unpausing. More complete example here: https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs – AaronLS Apr 08 '17 at 21:49