Wait for BackgroundWorker RunWorkerCompleted - c#

In the main thread I have a Timer. In the Tick event I run a BackgroundWorker. I do some things there and after that BackgroundWorker calls RunWorkerCompleted event.
In the main thread I have function Stop. This function disables the Timer. But I want wait for BackgroundWorker when he is working.
For example:
public void Next()
{
// Start the asynchronous operation
if (!this._backgroundWorker.IsBusy)
this._backgroundWorker.RunWorkerAsync();
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DoSomething();
}
private void _backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
}
public void Stop()
{
this._timer.Enabled = false;
}
So my question is how wait for RunWorkerCompleted event of BackgroundWorker? I need to wait until DoSomethingElse(); is finished.
Thanks

Handle the BackgroundWorker.RunWorkerCompleted event which occures when the background operation has completed, has been canceled, or has raised an exception.
// This event handler deals with the results of the
// background operation.
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
}
else
{
// Finally, handle the case where the operation
// succeeded.
}
}

If you only require two threads, allow the thread that called this._backgroundWorker.RunWorkerAsync();
to die after it calls this method and call anything you want to occur after DoSomethingElse(); within the same block as below
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
DoSomethingAfterSomethingElse();
}
Otherwise you are halting a thread to start another and then returning, which defeats the purpose of multiple threads?

I think BackgroundWorker.IsBusy property is the only member that can help you in this case. Hope below logic will do what you need.
//Add a class member
private bool stopped;
public void Stop()
{
if (!this._backgroundWorker.IsBusy)
{
this._timer.Enabled = false;
stopped = false;
}
else
{
stopped = true;
}
}
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
if (stopped)
{
this._timer.Enabled = false;
stopped = false;
}
}

Here is a way to stop/freeze the main thread until your background worker finishes:
public void Stop()
{
if (!_backgroundWorker.IsBusy)
{
_timer.Enabled = false;
// Stop/Freeze the main thread until the background worker finishes
while (_backgroundWorker.IsBusy)
{
Thread.Sleep(100);
}
}
}
Now if your application uses a form, I would just disable the whole form and show message letting the user know the the application is waiting for the process to finish. You can also have flag to disable the form from closing.
private bool _canClose;
public void Stop()
{
if (!_backgroundWorker.IsBusy)
{
_timer.Enabled = false;
// Don't let the user do anything in the form until the background worker finishes
this.IsEnabled = false;
_label.Text = "Waiting for the process to finish";
_canClose = false;
}
}
private void _backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
// Allow the user to close the form
this.IsEnabled = true;
_canClose = true;
}
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
e.Cancel = !_canClose;
}

Related

Waiting BackgoundWorker.DoWork() to finish

In our .NET Framework based application we save a huge file using a BackgroundWorker to keep UI responsive. When we close it, we don't want to stop the work in background (default behavior) and truncate the file.
Does a more elegant way to wait for its completion exist compared to this one?
while (this.backgroundWorker1.IsBusy)
{
// Keep UI messages moving, so the form remains
// responsive during the asynchronous operation.
Application.DoEvents();
}
Thanks.
EDIT: Basically, what we are trying to achieve, is to see the application to disappear and continue to see a process alive (in the Task Manager) until the background work is finished.
You can use a WaitHandle to keep synchronization with the worker thread.
private ManualResetEvent _canExit = new ManualResetEvent(true);
private DoBackgroundWork()
{
_canExit.Reset();
backgroundWorker1.RunWorkerAsync(_canExit);
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// This foreground thread will keep the process alive but allow UI thread to end.
new Thread(()=>
{
_canExit.WaitOne();
_canExit.Dispose();
}).Start();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ManualResetEvent mre = (ManualResetEvent )e.Argument;
// do your work.
mre.Set();
}
If you have multiple background thread to wait, manage a WaitHanlde collection and use WaitHandle.WaitAll to keep the process from exiting.
There is a difference in closing a form (what the user sees as the application) and closing the actual application. Assuming you currently have the background worker instance as a part of the form, you need to separate it into a different class. You can use a static class for a simple way to do this. Note this has a ManualResetEvent that will be fired when the worker is finished and an Action to override that the worker will invoke rather than specifying the DoWork event in the form.
public static class Background
{
public static ManualResetEvent WorkerResetEvent = new ManualResetEvent(false);
private static BackgroundWorker worker;
public static BackgroundWorker Worker
{
get
{
if (worker == null)
{
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
}
return worker;
}
}
private static void Worker_DoWork(object sender, DoWorkEventArgs e)
{
WorkerAction?.Invoke();
WorkerResetEvent.Set();
}
public static Action WorkerAction;
}
Now this isn't part of the form, it can persist after the form is closed. You can then keep the application running after the form has closed in a similar loop to the one you have. The simplest way to handle this is to have a 'parent' form that is not displayed but calls your existing form.
public class ParentForm : Form
{
private void ParentForm_Load(object sender, EventArgs e)
{
Visible = false;
ShowInTaskbar = false;
new Form1().ShowDialog(); // execution waits until form is closed
// now wait for worker to finish
while (Background.Worker.IsBusy)
{
Background.ResetEvent.WaitOne(5000); // Waits 5 seconds for the event
}
}
You'll need to call ParentForm instead of Form1 in the Program class:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ParentForm());
}
And to initialise the worker in your form. This also waits for the worker while updating the display but should quit out of the loop when the form is closed where the above loop will take over waiting.
public partial class Form1 : Form
{
private bool closing;
protected override void OnFormClosing(FormClosingEventArgs e)
{
closing = true;
base.OnFormClosing(e);
}
private void Save()
{
Background.WorkerAction = () =>
{
// your saving logic here
};
Background.Worker.RunWorkerAsync();
while (!closing && Background.Worker.IsBusy)
{
Background.WorkerResetEvent.WaitOne(500); // wait half a second at a time (up to you)
// any logic to update progress bar or other progress indicator
Refresh(); // Update the screen without costly DoEvents call
}
}
}
Clearly you missed this: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.runworkercompleted(v=vs.110).aspx there is a runworkercompleted - which is triggered on completion, cancelled or exception
bgworker.RunWorkerCompleted += JobCompleted;
private void JobCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
MessageBox.Show("Job cancelled");
}
else
{
do_next_stuff();
}
}

Deadlock when worker thread and UI thread calls the same method including invoke ui control

Here is my code depicts issue:
public partial class Form1 : Form
{
private readonly object lockObject = new object();
public Form1()
{
InitializeComponent();
}
private void btnWorkerThread_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => SomeLongRunningThread());
}
private void SomeLongRunningThread()
{
lock (lockObject)
{
Thread.Sleep(3000);
if (txtResult.InvokeRequired)
{
Thread.Sleep(3000);
txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); });
}
else
{
Thread.Sleep(3000);
txtResult.Text = DateTime.Now.ToShortTimeString();
}
}
}
private void btnUIThread_Click(object sender, EventArgs e)
{
SomeLongRunningThread();
}
}
When worker thread started and then btnUIThread clicked deadlock occurs:
It seems that deadlock occurs on txtResult.InvokeRequried:
Worker thread pass control to ui thread
As ui thread has the turn, it both updates txtResult and try to start btnUIThread click.
Deadlock occurs on line txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); });
Correct me if I am wrong.
The question is how to handle deadlock?
Edit: After valuable answers from fellows, my real projects is more complicated as expected, and lock block has more codes that I should guarantee executed by one thread at a time.
To avoid a deadlock you should lock differently. There's no point in locking all that threading code.
Moreover this lock (lockobject) is not necessary because in your sample SomeAction() is already synchronized to execute in the main/UI thread.
private void SomeLongRunningThread()
{
Thread.Sleep(3000);
if (txtResult.InvokeRequired)
{
Thread.Sleep(3000);
txtResult.Invoke((MethodInvoker) delegate { SomeAction });
}
else
{
Thread.Sleep(3000);
SomeAction();
}
}
private void SomeAction(){
// This lock is not needed as long this method is only called from SomeLongRunningThread()
//lock (lockObject)
//{
txtResult.Text = DateTime.Now.ToShortTimeString();
//}
}
There is no need to lock here at all.
Calling txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); }); pushes the delegate on to the UI message loop queue. When the loop is ready it pops the delegate and executes. The UI can only run one thing at once - No race and no deadlock.
.NET 4.5 introduced the IProgress< T> interface to report progress from threads and tasks, and the Progress< T> implementation which runs a callback or raises an event in the thread that created it. With this, and async/await, you don't need to call BeginInvoke or `Invoke.
This code can be simplified by using Progress :
public partial class Form1 : Form
{
IProgress<string> _progress;
public Form1()
{
InitializeComponent();
_progress = new Progress<string>(UpdateUI);
}
void UpdateUI(string message)
{
txtResult.Text = message;
}
private void btnWorkerThread_Click(object sender, EventArgs e)
{
Task.Run(() => SomeLongRunningThread());
}
private void SomeLongRunningThread()
{
Thread.Sleep(3000);
_progress.Report(DateTime.Now.ToShortTimeString());
}
private void btnUIThread_Click(object sender, EventArgs e)
{
SomeLongRunningThread();
}
}

Show busy loader while executing action

I have modified Background worker private AbortableBackgroundWorker _worker;
public class AbortableBackgroundWorker : BackgroundWorker
{
//Internal Thread
private Thread _workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (_workerThread != null)
{
_workerThread.Abort();
_workerThread = null;
}
}
}
And have method which init BgWorker
private void BusyLoader(Action doWorkAction)
{
if (_worker == null)
{
_worker = new AbortableBackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (sender, e) => _worker_DoWork(sender, e, doWorkAction);
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
}
if (!_worker.IsBusy)
_worker.RunWorkerAsync();
}
private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
loadingPanel.StopSpin();
_worker.Abort();
_worker.Dispose();
}
private void _worker_DoWork(object sender, DoWorkEventArgs e, Action action)
{
loadingPanel.StartSpin();
this.Dispatcher.Invoke(action);
}
When I call method BusyLoader I want to pass there Action, which should be executed and at this time busy Indicator should be shown.
I have tried It. And it seems to work but only for first call of BusyLoader. Because _worker.DoWork has the same method, as I understand.
How can I manage to change _worker.DoWork method for every new call of BusyLoader ? Or it is bad approach to pass Action like that?
You said it helped so will post comment as an answer
_worker is not null on the second call so _worker_DoWork is not redefined. Try removing and adding.

Prevent raising next events in a async event

When GetDataAsync executed, textBox1_Validating event is raised before textbox1_Leave event finished. how can I do to prevent this situation?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private bool IsValid = true;
private async void textBox1_Leave(object sender, EventArgs e)
{
MessageBox.Show("Working");
ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
IsValid = await client.CheckUser(textBox1.Text);
}
private void textBox1_Validating(object sender, CancelEventArgs e)
{
if(IsValid)
MessageBox.Show("Welcome!");
else
e.Cancel = true;
}
}
From Control.Validating:
events occur in the following order:
Enter
GotFocus
Leave
Validating
Validated
LostFocus
When you await inside Control.Leave, you let the UI message pump continue execution, hence it proccess the next event. If you want to wait until Leave finishes, run your method synchronously.
The Validating process of a control is a synchronous process, you can't have it wait until you return from your asynchronous method before continuing. The point of async / await is to allow the UI continue whilst you wait on the result of your asynchronous method therefore once you await inside the Leave event the control assumes it's complete and continues on with the rest of the event chain.
The Validating event should be used to perform synchronous validation, if you need server validation then you just have to accept that the text entered is valid and on then Validated event you could then send off your request
private bool IsValid = false;
private void textBox1_Validated(object sender, EventArgs e)
{
this.ValidateUser(textBox1.Text);
}
private async void ValidateUser(string username)
{
ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
IsValid = await client.CheckUser(textBox1.Text);
if (IsValid) {
MessageBox.Show("Welcome!");
} else {
MessageBox.Show("Invalid Username, try again!");
textBox1.Focus();
}
}

Events Fired in Background Thread Ignored

I'm running a C# forms application which starts a thread to acquire some data. This thread has some events inside it i.e: the events fire in the thread and are supposed to be captured by the same thread. However, the thread's events don't seem to be firing. Any clues?
private void btnPlay_Click(object sender, EventArgs e)
{
Thread thread = new Thread(kinect.onlineRun);
thread.IsBackground = true;
thread.Start();
}
inside the thread:
void PointCreated(object sender, IdEventArgs e) // a certain event that should fire and it doesn't
{
Console.WriteLine("Event Fired!");
}
public void onlinerun()
{
Console.WriteLine("run started"); // this is printed on console
while (true)
{
do_some_work();
//this work could result in the PointCreated event firing
}
}
Give something like this a try:
Assuming your calling class is called Controller and your delegate is called ControlEventHandler...
private void PointCreated(object sender, IdEventArgs e)
{
// Ensure the event was received in the calling thread
if (this.InvokeRequired)
{
if (e != null)
{
// We aren't in the correct thread so pass on the event
this.BeginInvoke(new Controller.ControllerEventHandler(this.PointCreated), new object[] { sender, e });
}
}
else
{
lock (this)
{
Console.WriteLine("Event Fired!");
// TODO: Do some stuff here
}
}
}

Categories