How to stop BackgroundWorker in form closing event?

I have a form that spawns BackgroundWorker, which should update the form of its own text field (in the main stream), hence Invoke((Action) (...)); call.
If in HandleClosingEvent I just do bgWorker.CancelAsync() , then I get an ObjectDisposedException on Invoke(...) call, ObjectDisposedException . But if I sit in the HandleClosingEvent and wait for bgWorker to be executed, then .Invoke (...) will never return, it’s also clear.

Any ideas how to close this application without getting an exception, or a dead end?

The following are 3 relevant methods of the simple class Form1:

  public Form1() { InitializeComponent(); Closing += HandleClosingEvent; this.bgWorker.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { while (!this.bgWorker.CancellationPending) { Invoke((Action) (() => { this.textBox1.Text = Environment.TickCount.ToString(); })); } } private void HandleClosingEvent(object sender, CancelEventArgs e) { this.bgWorker.CancelAsync(); /////// while (this.bgWorker.CancellationPending) {} // deadlock } 
+65
multithreading c # backgroundworker winforms
Nov 13 '09 at 19:32
source share
12 answers

The only safe and secure way I know is to actually cancel the FormClosing event. Set e.Cancel = true if BGW is still running, and set a flag to indicate that the user has requested to close. Then check this flag in the BGW RunWorkerCompleted event handler and call Close () if it is set.

 private bool closePending; protected override void OnFormClosing(FormClosingEventArgs e) { if (backgroundWorker1.IsBusy) { closePending = true; backgroundWorker1.CancelAsync(); e.Cancel = true; this.Enabled = false; // or this.Hide() return; } base.OnFormClosing(e); } void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (closePending) this.Close(); closePending = false; // etc... } 
+94
Nov 13 '09 at 22:41
source share

I found another way. If you have more background workers, you can do:

 List<Thread> bgWorkersThreads = new List<Thread>(); 

and in each case the PowerWorker make method:

 bgWorkesThreads.Add(Thread.CurrentThread); 

Arter that you can use:

 foreach (Thread thread in this.bgWorkersThreads) { thread.Abort(); } 

I used this in a Word add-in in the control that I use in CustomTaskPane . If someone closes the document or application earlier, then all my background work will finish their work, it raises some COM Exception (I don’t remember what exactly). CancelAsync() does not work.

But in doing so, I can close all the threads that backgroundworkers use backgroundworkers away in the DocumentBeforeClose event, and my problem is resolved.

+3
Mar 09 2018-12-12T00:
source share

Here is my solution (Sorry in VB.Net).

When I fire the FormClosing event, I fire BackgroundWorker1.CancelAsync () to set the CancellationPending parameter to True. Unfortunately, the program will never have the opportunity to check the CancellationPending value to set e.Cancel to true (which, as far as I can tell, can only be done in BackgroundWorker1_DoWork). I did not delete this line, although in reality it does not make any difference.

I added a line that would set my global variable bClosingForm to True. Then I added a line of code to my BackgroundWorker_WorkCompleted to check both e.Cancelled and the global variable bClosingForm before performing any final steps.

Using this template, you can close your form at any time, even if the background worker is in the middle of something (which may not be very good, but it must happen so that it can be considered), I'm not sure if this necessary, but you can completely save the Background worker from the Form_Closed event after that.

 Private bClosingForm As Boolean = False Private Sub SomeFormName_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing bClosingForm = True BackgroundWorker1.CancelAsync() End Sub Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork 'Run background tasks: If BackgroundWorker1.CancellationPending Then e.Cancel = True Else 'Background work here End If End Sub Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted If Not bClosingForm Then If Not e.Cancelled Then 'Completion Work here End If End If End Sub 
+2
Aug 12 '10 at 15:20
source share

Can you wait for the signal in the form destructor?

 AutoResetEvent workerDone = new AutoResetEvent(); private void HandleClosingEvent(object sender, CancelEventArgs e) { this.bgWorker.CancelAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { while (!this.bgWorker.CancellationPending) { Invoke((Action) (() => { this.textBox1.Text = Environment.TickCount.ToString(); })); } } private ~Form1() { workerDone.WaitOne(); } void backgroundWorker1_RunWorkerCompleted( Object sender, RunWorkerCompletedEventArgs e ) { workerDone.Set(); } 
+1
Nov 13 '09 at 21:01
source share

First, an ObjectDisposedException is just one possible error. Running the OP code threw the following InvalidOperationException in a significant number of cases:

Invoke or BeginInvoke cannot be invoked on a control until a window handle has been created.

I suppose this could be changed by starting the worker in the "Loaded" callback and not in the constructor, but all this testing can be avoided altogether if the BackgroundWorker Progress reporting mechanism is used. The following works well:

 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { while (!this.bgWorker.CancellationPending) { this.bgWorker.ReportProgress(Environment.TickCount); Thread.Sleep(1); } } private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.textBox1.Text = e.ProgressPercentage.ToString(); } 

I kind of hijacked a percentage parameter, but you can use another overload to pass any parameter.

It is interesting to note that removing the aforementioned sleep call clogs the user interface, consumes a high processor and constantly increases memory usage. I assume this is because the message queue is overloaded with a graphical interface. However, when the sleep call is intact, the CPU usage is actually 0, and memory usage seems fine. To be reasonable, perhaps a higher value than 1 ms should be used? An expert opinion will be appreciated here ... Update . It seems that while the update is not too frequent, it should be in order: Link

In any case, I cannot foresee a scenario where the GUI update should be within intervals not exceeding a couple of milliseconds (at least in scenarios where a person is watching the GUI), so I think most of what the time report will be the right choice

+1
Jan 25 2018-10-10T00:
source share

Your background worker should not use Invoke to update the text field. He should ask the UI thread to nicely update the text field using the ProgressChanged event with the value placed in the attached text field.

During the Closed event (or, possibly, the Closed event), the user interface thread remembers that the form is closed before it cancels the background worker.

After receiving progressChanged, the user interface thread checks if the form is closed, and only if not, it updates the text field.

0
Apr 18 '16 at 13:45
source share

This will not work for everyone, but if you periodically do something in BackgroundWorker, like every second or every 10 seconds (perhaps server polling), this seems to work well to stop the process in an orderly manner and without error messages ( at least so far) and easy to follow;

  public void StopPoll() { MyBackgroundWorker.CancelAsync(); //Cancel background worker AutoResetEvent1.Set(); //Release delay so cancellation occurs soon } private void bw_DoWork(object sender, DoWorkEventArgs e) { while (!MyBackgroundWorker.CancellationPending) { //Do some background stuff MyBackgroundWorker.ReportProgress(0, (object)SomeData); AutoResetEvent1.WaitOne(10000); } } 
0
Dec 18 '16 at 12:09
source share

I really don't understand why DoEvents is considered such a bad choice in this case if you use this.enabled = false. I think that would make it pretty neat.

 protected override void OnFormClosing(FormClosingEventArgs e) { this.Enabled = false; // or this.Hide() e.Cancel = true; backgroundWorker1.CancelAsync(); while (backgroundWorker1.IsBusy) { Application.DoEvents(); } e.cancel = false; base.OnFormClosing(e); } 
0
May 18 '18 at 23:57
source share

I would pass the SynchronizationContext associated with the text box to BackgroundWorker and use it to perform updates in the user interface thread. Using SynchronizationContext.Post, you can check if the control is installed or recycled.

-one
Nov 13 '09 at 19:48
source share

What about Me.IsHandleCreated?

  Private Sub BwDownload_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BwDownload.RunWorkerCompleted If Me.IsHandleCreated Then 'Form is still open, so proceed End If End Sub 
-one
Aug 01 '13 at
source share

One solution that works, but too complicated. The idea is to create a timer that will continue to try to close the form, and the form will not be closed until bgWorker is dead.

 private void HandleClosingEvent(object sender, CancelEventArgs e) { if (!this.bgWorker.IsBusy) { // bgWorker is dead, let Closing event proceed. e.Cancel = false; return; } if (!this.bgWorker.CancellationPending) { // it is first call to Closing, cancel the bgWorker. this.bgWorker.CancelAsync(); this.timer1.Enabled = true; } // either this is first attempt to close the form, or bgWorker isn't dead. e.Cancel = true; } private void timer1_Tick(object sender, EventArgs e) { Trace.WriteLine("Trying to close..."); Close(); } 
-2
Nov 13 '09 at 19:44
source share

Another way:

 if (backgroundWorker.IsBusy) { backgroundWorker.CancelAsync(); while (backgroundWorker.IsBusy) { Application.DoEvents(); } } 
-2
Apr 04 '12 at 18:17
source share



All Articles