Not creating new Backgroundworker instance - C# - c#

I am creating a progress form that utilizes a backgroundworker to run the process. It runs okay the first time the form is displayed, but after that I get the error
Additional information: This operation has already had OperationCompleted
called on it and further calls are illegal.
when I try to call the TheBackgroundworker.ReportProgress() method.
I am confused, because I am creating the progress form in a using block like this:
using (ProgressForm FPProgForm = new ProgressForm(TheUI))
{
FPProgForm.ShowDialog();
if (FPProgForm.DialogResult == DialogResult.OK)
{
// display results screen
}
}
And in the FPProgForm constructor, I am creating a new BackgroundWorker()
TheBackgroundworker = new BackgroundWorker();
So the BackGroundWorker should be brand new every time I create a new dialog.
Update: On request, here is the entire progress form class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FPDWF
{
public partial class ProgressForm : Form
{
public delegate void RunFunctionDelegate();
RunFunctionDelegate FuncToRun { get; } // function to be run
FPDesktopWFUI TheUI { get; }
BackgroundWorker TheBackgroundworker; // for internal use only, like a viagra demo
public ProgressForm(RunFunctionDelegate funcToRun, FPDesktopWFUI theUI)
{
InitializeComponent();
FuncToRun = funcToRun;
TheUI = theUI;
TheBackgroundworker = new BackgroundWorker();
InitializeBackgroundWorker();
// subscription to event stuff here: http://stackoverflow.com/questions/14871238/report-progress-backgroundworker-from-different-class-c-sharp
TheUI.OnProgressUpdate += FPProgUpdate;
}
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
// background worker stuff here: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
TheBackgroundworker.DoWork +=
new DoWorkEventHandler(TheBackgroundworker_DoWork);
TheBackgroundworker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(TheBackgroundworker_RunWorkerCompleted);
TheBackgroundworker.ProgressChanged +=
new ProgressChangedEventHandler(TheBackgroundworker_ProgressChanged);
TheBackgroundworker.WorkerReportsProgress = true;
TheBackgroundworker.WorkerSupportsCancellation = true;
}
private void ProgressForm_Load(object sender, EventArgs e)
{
// progress bar stuff here: http://stackoverflow.com/questions/12126889/how-to-use-winforms-progress-bar
ui_progbar.Maximum = 100;
ui_progbar.Step = 1;
ui_progbar.Value = 0;
TheBackgroundworker.RunWorkerAsync();
}
private void ui_cancelbutton_Click(object sender, EventArgs e)
{
if (TheBackgroundworker.WorkerSupportsCancellation == true)
{
// Cancel the asynchronous operation.
TheBackgroundworker.CancelAsync(); // there really is no purpose to this as i can just set the contRunning flag I think
TheUI.contRunning = false; // i think this thread safe due to 'volatile flag', https://msdn.microsoft.com/en-us/library/7a2f3ay4(v=vs.100).aspx
resultLabel.Text = "Cancelling...";
}
}
// This event handler is where the time-consuming work is done.
private void TheBackgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
FuncToRun();
}
// This event handler updates the progress.
private void TheBackgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// something to do here?
}
// This event handler deals with the results of the background operation.
private void TheBackgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (TheBackgroundworker.CancellationPending == true) // if (e.Cancelled == true)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
else if (e.Error != null)
{
this.DialogResult = DialogResult.Abort;
resultLabel.Text = "Error: " + e.Error.Message;
ui_viewres_btn.Text = "Close";
ui_viewres_btn.Enabled = true;
}
else
{
this.DialogResult = DialogResult.OK;
ui_viewres_btn.Enabled = true;
}
}
private void FPProgUpdate(string progText, double prog)
{
// utilizing this: http://stackoverflow.com/a/14871753/3661120
int intProg = Convert.ToInt32(prog * 100);
if (!TheBackgroundworker.CancellationPending)
{
TheBackgroundworker.ReportProgress(intProg); // doesn't really do anything at this point, but whatev
base.Invoke((Action)delegate
{
resultLabel.Text = progText;
ui_progbar.Value = intProg;
});
}
}
private void ui_viewres_btn_Click(object sender, EventArgs e)
{
this.Close(); // closes the window
}
}
}
Update 2: even when I remove the offending TheBackgroundworker.ReportProgress(intProg); line, I am still getting this error:
Additional information: Invoke or BeginInvoke cannot be called on a
control until the window handle has been created.

You retrieve this error because you are subscribing to this event:
TheUI.OnProgressUpdate += FPProgUpdate;
Therefore FPProgUpdate calls ReportProgress() multiple times.
As you have already noticed, the following like is not necessary and you can remove it:
TheBackgroundworker.ReportProgress(intProg);

Thanks to Marc for the help on this. The solution was that I needed to unsubscribe FPProgUpdate from the TheUI.OnProgressUpdate event in the disposal method, which I had to override:
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
if (components != null)
{
components.Dispose();
}
// Dispose stuff here
TheUI.OnProgressUpdate -= FPProgUpdate;
}
disposed = true;
base.Dispose(disposing);
}
The disposal does not automatically unsubscribe, it seems like.

TheBackgroundworker.ReportProgress should only be called from inside of the thread that is executing DoWork. From your code it looks like FPProgUpdate contains a ReportProgress and is being called from some thread other than the thread that started DoWork.

Related

Why does the FormClosing event produce a deadlock in a synchronized thread job using Monitors?

This is a bit difficult to explain. I have a thread instantiated within a WindowsForm, the thread is not a priority and does its job if nobody has taken the resource exclusively using Monitor.TryEnter.
But when closing the form I use the FormClosing event to stop the processing, save the processed data before closing, etc. When trying to take the resource with another Monitor.Enter .. the block is produced.
But the funny thing is that in the following way it works!!. Using a new/secondary thread or task to block the internal thread and perform the final job of save buffering.
"Minimal, Complete, and Verifiable example:"
namespace WindowsFormsApplication3
{
using System;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
public partial class Form1 : Form
{
private readonly object _syncRoot = new object();
private volatile bool _working = false;
readonly Label lblInfo = new Label();
public Form1()
{
InitializeComponent();
SuspendLayout();
lblInfo.Text = "...";
lblInfo.Location = new Point(0, 0);
Controls.Add(lblInfo);
CheckBox chkBox = new CheckBox();
chkBox.Text = "Use Task inside FormClosing";
chkBox.AutoSize = true;
chkBox.Location = new Point(Width / 2 - chkBox.Width / 2, Height / 2 + chkBox.Height);
chkBox.Click += ChkBox_Click; ;
Controls.Add(chkBox);
ResumeLayout(false);
FormClosing += Form1_FormClosing;
}
private CancellationTokenSource _cancellationTokenS;
private bool _flUseThread = false;
private void Form1_Load(object sender, EventArgs e)
{
_cancellationTokenS = new CancellationTokenSource();
CancellationToken token = _cancellationTokenS.Token;
_working = true;
Task.Run(() =>
{
while (_working)
{
if (token.IsCancellationRequested)
break;
processData(DateTime.Now);
}
}, token);
}
private void ChkBox_Click(object sender, EventArgs e)
{
_flUseThread = ((CheckBox)sender).Checked;
}
private bool _alreadySafedClose = false;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_flUseThread)
{
if (_alreadySafedClose) return;
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
e.Cancel = true; // Avoid close form.
Enabled = false; // Avoid interact with form.
Task.Factory.StartNew(() =>
{
bool lockWasTaken = false;
try
{
_working = false;
// Lock other thread.
Monitor.Enter(_syncRoot, ref lockWasTaken);
_cancellationTokenS.Cancel();
saveBufferResults();
MessageBox.Show("OK with Thread");
}
catch (Exception)
{
// ignored
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
Invoke((MethodInvoker)delegate
{
_alreadySafedClose = true;
Close(); //Finally Close.
});
}
});
}
else
e.Cancel = true;
}
else
{
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
bool lockWasTaken = false;
try
{
_working = false;
_cancellationTokenS.Cancel();
// Lock other thread avoid continue processing.
Monitor.Enter(_syncRoot, ref lockWasTaken); // <------ here it stays blocked!!
saveBufferResults();
MessageBox.Show("OK");
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
}
}
else
e.Cancel = true;
}
}
private void processData(DateTime data)
{
//flag to reduce locks..
if (!_working) return;
if (Monitor.TryEnter(_syncRoot))
{
try
{
//To escape quickly
if (_working)
innerProcessFrame(data);
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
Monitor.Exit(_syncRoot);
}
}
}
private ulong _dataCounter = 0;
private void innerProcessFrame(DateTime data)
{
Thread.Sleep(100);
Invoke((MethodInvoker)delegate
{
lblInfo.Text = data.Millisecond + " " + _dataCounter;
});
_dataCounter++;
}
private void saveBufferResults()
{
Thread.Sleep(1);
}
}
}
I believe that FormClosing is breaking the synchronization context or blocking the thread of work and
therefore in the event of the thread of work it never reaches the line where it releases the resource
using Monitor.Exit, in the processData(...) method:
My question is why does this happen only when closing the form within the FormClosing event?, because
if I put a button on the form and put the same blocking code everything stops correctly there is no DeadLock, and why does it work creating a secondary thread to do the final work when closing the form ?, and finally if someone knows a better or correct way to solve it.

Understanding BackgroundWorker

I would like to understand how BackgroundWorker is being used.
I have tried to break it down to a Console C# project, actually it is a Windows Form Application, where a button triggers the execution of three background tasks. When the button is pressed, it should become disabled preventing further button events. When all three tasks have finished, the button should be enabled again. Also the success of the three tasks should be tested in the main thread. To prevent from mixing this all into a Forms app, I'm trying now to understand the basics and move that to the Forms application.
(From the comments settings you may guess where my problems of understanding are)
Consider this (still erroneous) code:
using System;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
namespace BGWorkerConsoleApp
{
class Program
{
// #region BackgroundWorker
//
private System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
//
// #endregion
static void Main(string[] args)
{
// BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.RunWorkerCompleted += TestComplete;
worker.DoWork += TestConnection1;
// worker.DoWork += TestConnection2;
// worker.DoWork += TestConnection3;
DoWorkEventArgs e = new DoWorkEventArgs(); // ???
worker.RunWorkerAsync(e); // ???
}
private void TestConnection1(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(14000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection2(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection3(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(5000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("complete");
}
}
}
My questions:
Can I add more than one Task to a BackgroundWorker? Probably not, since there is only one RunWorkerAsync. Assuming I need a worker for every task, how would I wait for all three tasks to complete?
OK, let me change it to a Forms application since the stress doesn't lay on running BackGroundworker in a console App, rather I would like to understand how to design the application for more than one BackGroundWorkers and wait for their completion (including the passing and returning of parameters and results.
Here is the code for the same in a Form app:
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
namespace BackGroundWorkerForm
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private Button button;
/// <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.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
button = new System.Windows.Forms.Button();
button.Location = new System.Drawing.Point(100, 10);
button.Name = "button";
button.Size = new System.Drawing.Size(100, 20);
button.TabIndex = 5;
button.Text = "TestConnection";
button.Click += new System.EventHandler(this.RunTest);
button.Visible = true;
Controls.Add(button);
}
#endregion
private void RunTest(object o, EventArgs e)
{
BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.RunWorkerCompleted += TestComplete;
worker.DoWork += TestConnection1;
// worker.DoWork += TestConnection2;
// worker.DoWork += TestConnection3;
worker.RunWorkerAsync();
}
private void TestConnection1(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection2(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection3(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
button.Text= "complete";
}
}
}
Here is an example using Tasks instead of a background worker.
Each Task is started asynchronously, and then it waits for all to complete successfully or not before processing the results.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BGWorkerConsoleApp
{
class Program
{
static Stopwatch sw = new Stopwatch();
static void Main(string[] args)
{
// parse your args
int[] parsedargs = { 1400, 1000, 500 };
int count = 0; // to provide task count
List<Task> tasks = new List<Task>();
sw.Start(); //stopwatch for some to verify the time
foreach (int i in parsedargs)
{
// start a task for each
tasks.Add(Task.Factory.StartNew<bool>(
() => { return myTask(i, String.Format("Task{0} done. ", ++count)); } ) );
}
// wait for all the tasks to complete
Task.WaitAll(tasks.ToArray());
// check the response of each
bool faulted = false;
foreach (Task<bool> t in tasks)
{
if (t.Result == false)
{
faulted = true;
break; //there was a problem so quit looking
}
}
//output some text
if (faulted)
{
Console.WriteLine("There was a problem.");
}
else
Console.WriteLine("complete");
// pause so we can see our output in the debugger
Console.ReadKey();
}
static bool myTask(int time, string msg)
{
Thread.Sleep(time);
if (time == 1000)
return false;
Console.WriteLine(msg + printStopWatchTime());
return true;
}
static string printStopWatchTime()
{
TimeSpan ts = sw.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
return string.Format("RunTime {0}", elapsedTime);
}
}
}
For your first question: you can reuse a BackgroundWorker as long as you don't try to run the task while it's already runnig (i.e.: IsBusy must equal false). According to MSDN, if you try to do that, it will bite you hard with an InvalidOperationException.
On the second matter: you need some kind of synchronization mechanism to accomplish that. Check WaitHandle.WaitAll(...) and the Barrier class, for instance. There are plenty of options, not just those. You have monitors, semaphores, mutexes and whatnot at your disposal. Do explore the System.Threading namespace.
A BackgroundWorker is good if you want to execute one specific task in the background. That is, the DoWork event is assigned one handler that performs the task and finishes. Of course you can change the event method by removing the old one and assigning a new one, but the important part is that only one task can be performed at a time (you can not have the same background worker run more than once at the same time).
If you want to have two or more tasks to be performed at the same time, you either need more background workers, or you start looking into the Task Parallel Library, as suggested in the comments. Even plain Threads would work.
In either case, as Leandro said in his answer, what you're trying to achive screams for the use of some barrier, as otherwise the first finishing task will enable the button before the other tasks are finished. You, however, want to wait until all the tasks are finished.
I would like to add that in my opinion, use cases for the BackgroundWorker are pretty limited. While it is convenient in some cases, it does not provide the flexibility needed in most cases.

Delegate loads the Alert Form but I can't use any of the components.. its stuck

The problem is below. Here's my code...
// Contents of Form1.cs
// Usual includes
namespace ProcessMonitor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Boolean getStatus()
{
// Returns true if the system is active
if (label1.Text.Equals("Active"))
return true;
return false;
}
private void button1_Click(object sender, EventArgs e)
{
if(getStatus())
{
label1.Text = "Not Active";
button1.Text = "Activate";
}
else
{
label1.Text = "Active";
button1.Text = "Deactivate";
}
}
private void Form1_Load(object sender, EventArgs e)
{
Monitor mon = new Monitor(this);
mon.Run();
}
}
}
// Contents of Monitor.cs
// Usual includes
using System.Management;
using System.Diagnostics;
using System.Threading;
namespace ProcessMonitor
{
class Monitor
{
Form1 parent;
private void ShowAlert(Alert al)
{
al.Show();
}
public Monitor(Form1 parent)
{
this.parent = parent;
}
public void InvokeMethod()
{
//This function will be on main thread if called by Control.Invoke/Control.BeginInvoke
Alert frm = new Alert(this.parent);
frm.Show();
}
// This method that will be called when the thread is started
public void Run()
{
var query = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 0, 0, 1),
"TargetInstance isa \"Win32_Process\");
while (true)
{
using (var watcher = new ManagementEventWatcher(query))
{
ManagementBaseObject mo = watcher.WaitForNextEvent();a
//MessageBox.Show("Created process: " + ((ManagementBaseObject)mo["TargetInstance"])["Name"] + ",Path: " + ((ManagementBaseObject)mo["TargetInstance"])["ExecutablePath"]);
ManagementBaseObject o = (ManagementBaseObject)mo["TargetInstance"];
String str = "";
foreach (PropertyData s in o.Properties)
{
str += s.Name + ":" + s.Value + "\n";
}
this.parent.Invoke(new MethodInvoker(InvokeMethod), null);
}
}
}
}
}
Alert.cs is just a blank form with a label that says “new process has started”. I intend to display the name of the process and location, pid, etc. by passing it to this alert form via the Thread (i.e. class Monitor). I have deliberately made the thread load in form_load so that I can resolve this error first. Adding it as a thread properly after the main form loads fully is a later task. I need to fix this first..
The delegate creates the Alert form but I can’t click on it, its just stuck. Need help to solve this.
Your while loop in Run is blocking the UI thread.
by passing it to this alert form via the Thread
You never actually create a new thread or task here - you just run code which executes in the UI thread, and causes an infinite loop. This will prevent the main form, as well as your Alert form, from ever displaying messages.
You need to push this into a background thread in order for it to work, ie:
private void Form1_Load(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(_ =>
{
Monitor mon = new Monitor(this);
mon.Run();
});
}

How to close object of form created by Application.run() method in C#?

I am working on C# win form application.My problem is when i click on menu i created one separate thread which is showing the progress(splash progress form). When i abort thread, progress form still display..! but when i move mouse pointer over the form it disappear
immediately..!
Following is my code
Thread progressThread = new Thread(() => Application.Run(new frmOperationProgress()));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
progressThread.Abort();
How to close this progress form object in c#
The problem is using Abort - it's not generally recommended because there is no guarantee that it's going to do what you expect (in your case hide the form).
Best to add proper cancellation support into your thread and handle hiding the splash screen directly.
Please never ever use Abort(). This kind of work is best done trough BackgroundWorker; if you insist on Thread
Try:
var form = new frmOperationProgress();
Thread progressThread = new Thread(() => Application.Run(form));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
form.ExternalClose();
Where ExternalClose is method of form like this:
public void ExternalClose() {
if (InvokeRequired) {
Invoke(new MethodInvoker(() => { ExternalClose(); }));
} else {
Close();
}
}
Solution using BackgroundWorker:
In backround worker you have to do UI stuff in ProgressChanged event (which is running in UI thread) and do the dirty work in DoWork event (background thread).
FormMain.cs: (Form with single BackgroundWorker control, named "backgroundWorker1", with wired up events backgroundWorker1_DoWork, backgroundWorker1_ProgressChanged and WorkerReportsProgress set to true)
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleApplication1 {
public partial class FormMain : Form {
private FormProgress m_Form;
public FormMain() {
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
backgroundWorker1.ReportProgress(0, "hello");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(20, "world");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(40, "this");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(60, "is");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(80, "simple");
backgroundWorker1.ReportProgress(100, "end");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
if (e.ProgressPercentage == 0 && m_Form == null) {
m_Form = new FormProgress();
m_Form.Show();
}
if (e.ProgressPercentage == 100 && m_Form != null) {
m_Form.Close();
m_Form = null;
return;
}
var message = (string)e.UserState;
m_Form.UpdateProgress(e.ProgressPercentage, message);
}
}
}
Where FormProgress is simple form with ProgressBar progressBar1 and Label label1 and one extra method:
public void UpdateProgress(int percentage, string message) {
this.progressBar1.Value = percentage;
this.label1.Text = message;
}
You can just close your form and the thread (which is blocked by the message loop of that form) will be ended naturally:
var yourForm = new frmOperationProgress();
//Start it
Thread progressThread = new Thread(() => Application.Run(yourForm));
progressThread.IsBackground = true;
progressThread.Start();
//....
//close it
yourForm.Invoke((Action)(() => yourForm.Close()));

How to wait for a BackgroundWorker to cancel?

Consider a hypothetical method of an object that does stuff for you:
public class DoesStuff
{
BackgroundWorker _worker = new BackgroundWorker();
...
public void CancelDoingStuff()
{
_worker.CancelAsync();
//todo: Figure out a way to wait for BackgroundWorker to be cancelled.
}
}
How can one wait for a BackgroundWorker to be done?
In the past people have tried:
while (_worker.IsBusy)
{
Sleep(100);
}
But this deadlocks, because IsBusy is not cleared until after the RunWorkerCompleted event is handled, and that event can't get handled until the application goes idle. The application won't go idle until the worker is done. (Plus, it's a busy loop - disgusting.)
Others have add suggested kludging it into:
while (_worker.IsBusy)
{
Application.DoEvents();
}
The problem with that is that is Application.DoEvents() causes messages currently in the queue to be processed, which cause re-entrancy problems (.NET isn't re-entrant).
I would hope to use some solution involving Event synchronization objects, where the code waits for an event - that the worker's RunWorkerCompleted event handlers sets. Something like:
Event _workerDoneEvent = new WaitHandle();
public void CancelDoingStuff()
{
_worker.CancelAsync();
_workerDoneEvent.WaitOne();
}
private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
_workerDoneEvent.SetEvent();
}
But I'm back to the deadlock: the event handler can't run until the application goes idle, and the application won't go idle because it's waiting for an Event.
So how can you wait for an BackgroundWorker to finish?
Update
People seem to be confused by this question. They seem to think that I will be using the BackgroundWorker as:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyWork;
worker.RunWorkerAsync();
WaitForWorkerToFinish(worker);
That is not it, that is not what I'm doing, and that is not what is being asked here. If that were the case, there would be no point in using a background worker.
If I understand your requirement right, you could do something like this (code not tested, but shows the general idea):
private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
}
public void Cancel()
{
worker.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(!e.Cancel)
{
// do something
}
_resetEvent.Set(); // signal that worker is done
}
There is a problem with this response. The UI needs to continue to process messages while you are waiting, otherwise it will not repaint, which will be a problem if your background worker takes a long time to respond to the cancel request.
A second flaw is that _resetEvent.Set() will never be called if the worker thread throws an exception - leaving the main thread waiting indefinitely - however this flaw could easily be fixed with a try/finally block.
One way to do this is to display a modal dialog which has a timer that repeatedly checks if the background worker has finished work (or finished cancelling in your case). Once the background worker has finished, the modal dialog returns control to your application. The user can't interact with the UI until this happens.
Another method (assuming you have a maximum of one modeless window open) is to set ActiveForm.Enabled = false, then loop on Application,DoEvents until the background worker has finished cancelling, after which you can set ActiveForm.Enabled = true again.
Almost all of you are confused by the question, and are not understanding how a worker is used.
Consider a RunWorkerComplete event handler:
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
rocketOnPad = false;
label1.Text = "Rocket launch complete.";
}
else
{
rocketOnPad = true;
label1.Text = "Rocket launch aborted.";
}
worker = null;
}
And all is good.
Now comes a situation where the caller needs to abort the countdown because they need to execute an emergency self-destruct of the rocket.
private void BlowUpRocket()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
StartClaxon();
SelfDestruct();
}
And there is also a situation where we need to open the access gates to the rocket, but not while doing a countdown:
private void OpenAccessGates()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
if (!rocketOnPad)
DisengageAllGateLatches();
}
And finally, we need to de-fuel the rocket, but that's not allowed during a countdown:
private void DrainRocket()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
if (rocketOnPad)
OpenFuelValves();
}
Without the ability to wait for a worker to cancel, we must move all three methods to the RunWorkerCompletedEvent:
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
rocketOnPad = false;
label1.Text = "Rocket launch complete.";
}
else
{
rocketOnPad = true;
label1.Text = "Rocket launch aborted.";
}
worker = null;
if (delayedBlowUpRocket)
BlowUpRocket();
else if (delayedOpenAccessGates)
OpenAccessGates();
else if (delayedDrainRocket)
DrainRocket();
}
private void BlowUpRocket()
{
if (worker != null)
{
delayedBlowUpRocket = true;
worker.CancelAsync();
return;
}
StartClaxon();
SelfDestruct();
}
private void OpenAccessGates()
{
if (worker != null)
{
delayedOpenAccessGates = true;
worker.CancelAsync();
return;
}
if (!rocketOnPad)
DisengageAllGateLatches();
}
private void DrainRocket()
{
if (worker != null)
{
delayedDrainRocket = true;
worker.CancelAsync();
return;
}
if (rocketOnPad)
OpenFuelValves();
}
Now I could write my code like that, but I'm just not gonna. I don't care, I'm just not.
You can check into the RunWorkerCompletedEventArgs in the RunWorkerCompletedEventHandler to see what the status was. Success, canceled or an error.
private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
{
Console.WriteLine("The worker was cancelled.");
}
}
Update: To see if your worker has called .CancelAsync() by using this:
if (_worker.CancellationPending)
{
Console.WriteLine("Cancellation is pending, no need to call CancelAsync again");
}
You don't wait for the background worker to complete. That pretty much defeats the purpose of launching a separate thread. Instead, you should let your method finish, and move any code that depends on completion to a different place. You let the worker tell you when it's done and call any remaining code then.
If you want to wait for something to complete use a different threading construct that provides a WaitHandle.
Why can't you just tie into the BackgroundWorker.RunWorkerCompleted Event. It's a callback that will "Occur when the background operation has completed, has been canceled, or has raised an exception."
I don't understand why you'd want to wait for a BackgroundWorker to complete; it really seems like the exact opposite of the motivation for the class.
However, you could start every method with a call to worker.IsBusy and have them exit if it is running.
Hm maybe I am not getting your question right.
The backgroundworker calls the WorkerCompleted event once his 'workermethod' (the method/function/sub that handles the backgroundworker.doWork-event) is finished so there is no need for checking if the BW is still running.
If you want to stop your worker check the cancellation pending property inside your 'worker method'.
The workflow of a BackgroundWorker object basically requires you to handle the RunWorkerCompleted event for both normal execution and user cancellation use cases. This is why the property RunWorkerCompletedEventArgs.Cancelled exists. Basically, doing this properly requires that you consider your Cancel method to be an asynchronous method in itself.
Here's an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
namespace WindowsFormsApplication1
{
public class AsyncForm : Form
{
private Button _startButton;
private Label _statusLabel;
private Button _stopButton;
private MyWorker _worker;
public AsyncForm()
{
var layoutPanel = new TableLayoutPanel();
layoutPanel.Dock = DockStyle.Fill;
layoutPanel.ColumnStyles.Add(new ColumnStyle());
layoutPanel.ColumnStyles.Add(new ColumnStyle());
layoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
layoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
_statusLabel = new Label();
_statusLabel.Text = "Idle.";
layoutPanel.Controls.Add(_statusLabel, 0, 0);
_startButton = new Button();
_startButton.Text = "Start";
_startButton.Click += HandleStartButton;
layoutPanel.Controls.Add(_startButton, 0, 1);
_stopButton = new Button();
_stopButton.Enabled = false;
_stopButton.Text = "Stop";
_stopButton.Click += HandleStopButton;
layoutPanel.Controls.Add(_stopButton, 1, 1);
this.Controls.Add(layoutPanel);
}
private void HandleStartButton(object sender, EventArgs e)
{
_stopButton.Enabled = true;
_startButton.Enabled = false;
_worker = new MyWorker() { WorkerSupportsCancellation = true };
_worker.RunWorkerCompleted += HandleWorkerCompleted;
_worker.RunWorkerAsync();
_statusLabel.Text = "Running...";
}
private void HandleStopButton(object sender, EventArgs e)
{
_worker.CancelAsync();
_statusLabel.Text = "Cancelling...";
}
private void HandleWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
_statusLabel.Text = "Cancelled!";
}
else
{
_statusLabel.Text = "Completed.";
}
_stopButton.Enabled = false;
_startButton.Enabled = true;
}
}
public class MyWorker : BackgroundWorker
{
protected override void OnDoWork(DoWorkEventArgs e)
{
base.OnDoWork(e);
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(500);
if (this.CancellationPending)
{
e.Cancel = true;
e.Result = false;
return;
}
}
e.Result = true;
}
}
}
If you really really don't want your method to exit, I'd suggest putting a flag like an AutoResetEvent on a derived BackgroundWorker, then override OnRunWorkerCompleted to set the flag. It's still kind of kludgy though; I'd recommend treating the cancel event like an asynchronous method and do whatever it's currently doing in the RunWorkerCompleted handler.
I'm a little late to the party here (about 4 years) but what about setting up an asynchronous thread that can handle a busy loop without locking the UI, then have the callback from that thread be the confirmation that the BackgroundWorker has finished cancelling?
Something like this:
class Test : Form
{
private BackgroundWorker MyWorker = new BackgroundWorker();
public Test() {
MyWorker.DoWork += new DoWorkEventHandler(MyWorker_DoWork);
}
void MyWorker_DoWork(object sender, DoWorkEventArgs e) {
for (int i = 0; i < 100; i++) {
//Do stuff here
System.Threading.Thread.Sleep((new Random()).Next(0, 1000)); //WARN: Artificial latency here
if (MyWorker.CancellationPending) { return; } //Bail out if MyWorker is cancelled
}
}
public void CancelWorker() {
if (MyWorker != null && MyWorker.IsBusy) {
MyWorker.CancelAsync();
System.Threading.ThreadStart WaitThread = new System.Threading.ThreadStart(delegate() {
while (MyWorker.IsBusy) {
System.Threading.Thread.Sleep(100);
}
});
WaitThread.BeginInvoke(a => {
Invoke((MethodInvoker)delegate() { //Invoke your StuffAfterCancellation call back onto the UI thread
StuffAfterCancellation();
});
}, null);
} else {
StuffAfterCancellation();
}
}
private void StuffAfterCancellation() {
//Things to do after MyWorker is cancelled
}
}
In essence what this does is fire off another thread to run in the background that just waits in it's busy loop to see if the MyWorker has completed. Once MyWorker has finished cancelling the thread will exit and we can use it's AsyncCallback to execute whatever method we need to follow the successful cancellation - it'll work like a psuedo-event. Since this is separate from the UI thread it will not lock the UI while we wait for MyWorker to finish cancelling. If your intention really is to lock and wait for the cancel then this is useless to you, but if you just want to wait so you can start another process then this works nicely.
I know this is really late (5 years) but what you are looking for is to use a Thread and a SynchronizationContext. You are going to have to marshal UI calls back to the UI thread "by hand" rather than let the Framework do it auto-magically.
This allows you to use a Thread that you can Wait for if needs be.
Imports System.Net
Imports System.IO
Imports System.Text
Public Class Form1
Dim f As New Windows.Forms.Form
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
Dim l As New Label
l.Text = "Please Wait"
f.Controls.Add(l)
l.Dock = DockStyle.Fill
f.StartPosition = FormStartPosition.CenterScreen
f.FormBorderStyle = Windows.Forms.FormBorderStyle.None
While BackgroundWorker1.IsBusy
f.ShowDialog()
End While
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim i As Integer
For i = 1 To 5
Threading.Thread.Sleep(5000)
BackgroundWorker1.ReportProgress((i / 5) * 100)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.Text = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
f.Close()
End Sub
End Class
Fredrik Kalseth's solution to this problem is the best I've found so far. Other solutions use Application.DoEvent() that can cause problems or simply don't work. Let me cast his solution into a reusable class. Since BackgroundWorker is not sealed, we can derive our class from it:
public class BackgroundWorkerEx : BackgroundWorker
{
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
private bool _resetting, _started;
private object _lockObject = new object();
public void CancelSync()
{
bool doReset = false;
lock (_lockObject) {
if (_started && !_resetting) {
_resetting = true;
doReset = true;
}
}
if (doReset) {
CancelAsync();
_resetEvent.WaitOne();
lock (_lockObject) {
_started = false;
_resetting = false;
}
}
}
protected override void OnDoWork(DoWorkEventArgs e)
{
lock (_lockObject) {
_resetting = false;
_started = true;
_resetEvent.Reset();
}
try {
base.OnDoWork(e);
} finally {
_resetEvent.Set();
}
}
}
With flags and proper locking, we make sure that _resetEvent.WaitOne() really gets only called if some work has been started, otherwise _resetEvent.Set(); might never been called!
The try-finally ensures that _resetEvent.Set(); will be called, even if an exception should occur in our DoWork-handler. Otherwise the application could freeze forever when calling CancelSync!
We would use it like this:
BackgroundWorkerEx _worker;
void StartWork()
{
StopWork();
_worker = new BackgroundWorkerEx {
WorkerSupportsCancellation = true,
WorkerReportsProgress = true
};
_worker.DoWork += Worker_DoWork;
_worker.ProgressChanged += Worker_ProgressChanged;
}
void StopWork()
{
if (_worker != null) {
_worker.CancelSync(); // Use our new method.
}
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 20; i++) {
if (worker.CancellationPending) {
e.Cancel = true;
break;
} else {
// Simulate a time consuming operation.
System.Threading.Thread.Sleep(500);
worker.ReportProgress(5 * i);
}
}
}
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressLabel.Text = e.ProgressPercentage.ToString() + "%";
}
You can also add a handler to the RunWorkerCompleted event as shown here:
BackgroundWorker Class (Microsoft documentation).
Just wanna say I came here because I need a background worker to wait while I was running an async process while in a loop, my fix was way easier than all this other stuff^^
foreach(DataRow rw in dt.Rows)
{
//loop code
while(!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}
Just figured I'd share because this is where I ended up while searching for a solution. Also, this is my first post on stack overflow so if its bad or anything I'd love critics! :)
Closing the form closes my open logfile. My background worker writes that logfile, so I can't let MainWin_FormClosing() finish until my background worker terminates. If I don't wait for my background worker to terminate, exceptions happen.
Why is this so hard?
A simple Thread.Sleep(1500) works, but it delays shutdown (if too long), or causes exceptions (if too short).
To shut down right after the background worker terminates, just use a variable. This is working for me:
private volatile bool bwRunning = false;
...
private void MainWin_FormClosing(Object sender, FormClosingEventArgs e)
{
... // Clean house as-needed.
bwInstance.CancelAsync(); // Flag background worker to stop.
while (bwRunning)
Thread.Sleep(100); // Wait for background worker to stop.
} // (The form really gets closed now.)
...
private void bwBody(object sender, DoWorkEventArgs e)
{
bwRunning = true;
BackgroundWorker bw = sender as BackgroundWorker;
... // Set up (open logfile, etc.)
for (; ; ) // infinite loop
{
...
if (bw.CancellationPending) break;
...
}
... // Tear down (close logfile, etc.)
bwRunning = false;
} // (bwInstance dies now.)
You can piggy back off of the RunWorkerCompleted event. Even if you've already added an event handler for _worker, you can add another an they will execute in the order in which they were added.
public class DoesStuff
{
BackgroundWorker _worker = new BackgroundWorker();
...
public void CancelDoingStuff()
{
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) =>
{
// do whatever you want to do when the cancel completes in here!
});
_worker.CancelAsync();
}
}
this could be useful if you have multiple reasons why a cancel may occur, making the logic of a single RunWorkerCompleted handler more complicated than you want. For instance, cancelling when a user tries to close the form:
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_worker != null)
{
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => this.Close());
_worker.CancelAsync();
e.Cancel = true;
}
}
I use async method and await to wait for the worker finishing its job:
public async Task StopAsync()
{
_worker.CancelAsync();
while (_isBusy)
await Task.Delay(1);
}
and in DoWork method:
public async Task DoWork()
{
_isBusy = true;
while (!_worker.CancellationPending)
{
// Do something.
}
_isBusy = false;
}
You may also encapsulate the while loop in DoWork with try ... catch to set _isBusy is false on exception. Or, simply check _worker.IsBusy in the StopAsync while loop.
Here is an example of full implementation:
class MyBackgroundWorker
{
private BackgroundWorker _worker;
private bool _isBusy;
public void Start()
{
if (_isBusy)
throw new InvalidOperationException("Cannot start as a background worker is already running.");
InitialiseWorker();
_worker.RunWorkerAsync();
}
public async Task StopAsync()
{
if (!_isBusy)
throw new InvalidOperationException("Cannot stop as there is no running background worker.");
_worker.CancelAsync();
while (_isBusy)
await Task.Delay(1);
_worker.Dispose();
}
private void InitialiseWorker()
{
_worker = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
_worker.DoWork += WorkerDoWork;
}
private void WorkerDoWork(object sender, DoWorkEventArgs e)
{
_isBusy = true;
try
{
while (!_worker.CancellationPending)
{
// Do something.
}
}
catch
{
_isBusy = false;
throw;
}
_isBusy = false;
}
}
To stop the worker and wait for it runs to the end:
await myBackgroundWorker.StopAsync();
The problems with this method are:
You have to use async methods all the way.
await Task.Delay is inaccurate. On my PC, Task.Delay(1) actually waits ~20ms.
oh man, some of these have gotten ridiculously complex. all you need to do is check the BackgroundWorker.CancellationPending property inside the DoWork handler. you can check it at any time. once it's pending, set e.Cancel = True and bail from the method.
// method here
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (sender as BackgroundWorker);
// do stuff
if(bw.CancellationPending)
{
e.Cancel = True;
return;
}
// do other stuff
}

Categories

Resources