Tom said:
Almost. If I try to reuse the background Worker too soon, it will call
DoWork (for the new thread) before RunWorkerCompleted (from the old
thread) finishes and the new thread will think it was canceled before
it gets any work done.
That doesn't make sense. Calling RunWorkerAsync() resets the
CancellationPending flag; if you are finding it still set in your DoWork
event handler, the only way for that to happen is for some code
somewhere to actually cancel the worker _after_ you've called
RunWorkerAsync().
Should I create a new BackgroundWorker object after canceling the old
one?
It's fine to reuse the BackgroundWorker instance. Just don't do it
until the first task is done.
As I wrote before, you don't need to have your thread wait for the
worker. Just put whatever logic you want executed when it's done in the
RunWorkerCompleted event handler. If you do it correctly, it will work
fine. If you don't do it correctly, well…all sorts of things can go
wrong.
See below for a short code example of doing it correctly. It needs
project references to the System, System.Drawing, and
System.Windows.Forms libraries.
Pete
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
namespace TestBackgroundWorkerRestart
{
class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private bool _fContinue;
private BackgroundWorker _worker;
private void button1_Click(object sender, EventArgs e)
{
_fContinue = true;
button1.Enabled = false;
button2.Enabled = true;
button3.Enabled = true;
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.ProgressChanged += (sender1, e1) =>
{
textBox1.AppendText((string)e1.UserState);
};
_worker.DoWork += (sender1, e1) =>
{
bool fReported = false;
while (!_worker.CancellationPending)
{
if (!fReported)
{
_worker.ReportProgress(0, "Worker started...no
cancellation pending\n");
fReported = true;
}
Thread.Sleep(250);
}
if (!fReported)
{
_worker.ReportProgress(0, "Worker cancelled before
entering loop\n");
}
};
_worker.RunWorkerCompleted += (sender1, e1) =>
{
textBox1.AppendText("Worker completed\n");
if (_fContinue)
{
_worker.RunWorkerAsync();
}
else
{
textBox1.AppendText("Not restarting worker\n");
_worker = null;
button1.Enabled = true;
button2.Enabled = false;
button3.Enabled = false;
}
};
_worker.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
_worker.CancelAsync();
}
private void button3_Click(object sender, EventArgs e)
{
_fContinue = false;
_worker.CancelAsync();
}
#region Code from Form1.Designer.cs
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be
disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 12);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "Start Worker";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new
System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Enabled = false;
this.button2.Location = new System.Drawing.Point(93, 12);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(90, 23);
this.button2.TabIndex = 1;
this.button2.Text = "Cancel Worker";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new
System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Enabled = false;
this.button3.Location = new System.Drawing.Point(189, 12);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(75, 23);
this.button3.TabIndex = 2;
this.button3.Text = "Stop Worker";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new
System.EventHandler(this.button3_Click);
//
// textBox1
//
this.textBox1.Anchor =
((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.Location = new System.Drawing.Point(12, 41);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.ReadOnly = true;
this.textBox1.ScrollBars =
System.Windows.Forms.ScrollBars.Both;
this.textBox1.Size = new System.Drawing.Size(335, 209);
this.textBox1.TabIndex = 3;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(359, 262);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.TextBox textBox1;
#endregion
}
}