Daniel Lip
Daniel Lip

Reputation: 11341

How to pause and resume a BackgroundWorker?

This is how I did it in my code: In the backgroundWorker DoWork event I did:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            _busy.WaitOne();
                this.Invoke(new MethodInvoker(delegate { label2.Text = "Website To Crawl: "; }));
                this.Invoke(new MethodInvoker(delegate { label4.Text = mainUrl; }));
                webCrawler(mainUrl, levelsToCrawl, e);


        }

Then in the pause button click event I did:

private void button4_Click(object sender, EventArgs e)
        {
            _busy.Reset();
        }

In the resume button click event I did:

private void button5_Click(object sender, EventArgs e)
        {
            _busy.Set();
        }

But it's not working when I click to start the process:

private void button1_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
            button1.Enabled = false;
            this.Text = "Processing...";
            label6.Text = "Processing...";
            label6.Visible = true;
            button2.Enabled = false;
            checkBox1.Enabled = false;
            checkBox2.Enabled = false;
            numericUpDown1.Enabled = false;
            button3.Enabled = true;
        }

Nothing happen only when I click the resume button the process start then when I click the pause button nothing happen.

I want that when I click the start process button it will start the backgroundWorker regular then when clicking the pause button it will pause and the resume button it will resume.

What did I do wrong ? Can someone fix my code ?

Upvotes: 7

Views: 28312

Answers (5)

dynamichael
dynamichael

Reputation: 852

I use a simple class that utilizes System.Thread.Monitor and lock()...

public class ThreadPauseState {
    private object _lock = new object();
    private bool _paused = false;

    public bool Paused {
        get { return _paused; }
        set {
            if(_paused != value) {
                if(value) {
                    Monitor.Enter(_lock);
                    _paused = true;
                } else {
                    _paused = false;
                    Monitor.Exit(_lock);
                }
            }
        }
    }

    public void Wait() {
        lock(_lock) { }
    }
}

Using it is very simple...

private ThreadPauseState _state = new ThreadPauseState();

private void btnPause_Click(object sender, EventArgs e) {
    _state.Paused = true;
}

private void btnResume_Click(object sender, EventArgs e) {
    _state.Paused = false;
}

private void btnCancel_Click(object sender, EventArgs e) {
    backgroundWorker1.CancelAsync();
    _state.Paused = false; // needed if you cancel while paused
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
    var worker = (BackgroundWorker)sender;
    for(var _ = 0; _ < 100; _++) {
        _state.Wait();
        if(worker.CancellationPending) return;
        Thread.Sleep(100); // or whatever your work is
    }
}

Upvotes: 1

mb777
mb777

Reputation: 143

This works for me:

bool work = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += myChangeFunction;
backgroundWorker1.RunWorkerAsync();

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    while (true && work)
    {
        // Your code here

        backgroundWorker1.ReportProgress(0);
        Thread.Sleep(1000);
    }
    e.Cancel = true;
}
private void myChangeFunction(object sender, ProgressChangedEventArgs e)
{
  // Here you can change label.text or whatever thing the interface needs to change.
}
private void Stop()
{
    work = false;
}
private void Start()
{
    work = true;
    backgroundWorker1.RunWorkerAsync();
}

NOTE: If you want to change something of the interface, you have to put it in the myChangeFunction(), because in the DoWork() function will not work. Hope this helps.

Upvotes: -1

Nikko Sunglao
Nikko Sunglao

Reputation: 47

I've been looking for the answer of this thread but I come up with my own solution i made and i just wanna share it with you. hope this works.

I have a background worker and i want to pause it when i hit close button of my form. asking "You are about to cancel the process" so it should pause the process.

declare bool pauseWorker = false; on your class.

private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
   while (condition)
   {
      if (pauseWorker == true)
      {
         while (pauseWorker == true)
         {
            if (pauseWorker == false) break;
         }
      }
      else
      {
         //continue process... your code here
      }
   }
}

private void frmCmsnDownload_FormClosing(object sender, FormClosingEventArgs e)
{
   if (bgWorker.IsBusy)
   {
      pauseWorker = true; //this will trigger the dowork event to loop that
                          //checks if pauseWorker is set to false
      DiaglogResult x = MessageBox.Show("You are about cancel the process", "Close", MessageBoxButtons.YesNo);
      if (x == DialogResult.Yes) bgWorker.CancelAsync();
      else 
      { 
         e.Cancel = true; 
         pauseWorker = false; //if the user click no
                              //the do work will continue the process
         return;
      }
   }
}

Therefore the main solution here is the boolean declaration that controls the DoWork event of BGWorker. Hope this solution helps your problem. Thank you.

Upvotes: 0

Mike Perrenoud
Mike Perrenoud

Reputation: 67948

You should be able to do this using the ManualResetEvent like this ...

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{
    _busy.WaitOne();
    test(mainUrl, levelsToCrawl, e); 
}

... and then when you want to pause the thread call _busy.Reset() ... and when you want to restart it call _busy.Set().

Additionally, you can place _busy.WaitOne(); anywhere you want to pause.

Upvotes: 0

dbattaglia
dbattaglia

Reputation: 678

In your BackgroundWorker thread code, you need to find places that are safe to "pause" execution. The ManualResetEvent is the right way to code. This other post might help: Is there a way to indefinitely pause a thread?

Basically, in a few choice points in your worker thread code where you want to allow it to pause, try inserting:

_busy.WaitOne(Timeout.Infinite);

And when you want to pause (from your main thread) you use:

_busy.Reset();

And to resume:

_busy.Set();

Upvotes: 11

Related Questions