I have a form that spawns a BackgroundWorker, that should update form's own textbox (on main thread), hence Invoke((Action) (...));
call.
If in HandleClosingEvent
I just do bgWorker.CancelAsync()
then I get ObjectDisposedException
on Invoke(...)
call, understandably. But if I sit in HandleClosingEvent
and wait for bgWorker to be done, than .Invoke(...) never returns, also understandably.
Any ideas how do I close this app without getting the exception, or the deadlock?
Following are 3 relevant methods of the simple Form1 class:
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 }
In the FormClosing event of the main application wait until the IsBusy property of the BackgroundWorker becomes FALSE. A better implementation is to cancel the form closing and to start a custom timer which checks every N milliseconds the IsBusy property.
On top of the Properties window, click the Events icon. Double-click the FormClosing event. Visual C# creates an empty event handler method and adds it to your code automatically. If your form’s name was Form1 (which is the default form name) you should find a new method called Form1_FormClosing in your code.
If your BackgroundWorker thread takes a while to cancel, you might think about checking for cancel more often. If the form has hooked up some event handlers to the BackgroundWorker thread, you cannot let it close before the BackgroundWorker is done; otherwise the BackgroundWorker will be calling into an object that is in an unknown state.
Double-click the FormClosing event. Visual C# creates an empty event handler method and adds it to your code automatically. If your form’s name was Form1 (which is the default form name) you should find a new method called Form1_FormClosing in your code.
The only deadlock-safe and exception-safe way to do this that I know is to actually cancel the FormClosing event. Set e.Cancel = true if the BGW is still running and set a flag to indicate that the user requested a close. Then check that flag in the BGW's 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... }
I've found another way. If you have more backgroundWorkers you can make:
List<Thread> bgWorkersThreads = new List<Thread>();
and in every backgroundWorker's DoWork method make:
bgWorkesThreads.Add(Thread.CurrentThread);
Arter that you can use:
foreach (Thread thread in this.bgWorkersThreads) { thread.Abort(); }
I used this in Word Add-in in Control, which i use in CustomTaskPane
. If someone close the document or application earlier then all my backgroundWorkes finishes their work, it raises some COM Exception
(I don't remember exatly which).CancelAsync()
doesn't work.
But with this, I can close all threads which are used by backgroundworkers
Immediately in DocumentBeforeClose
event and my problem is solved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With