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();
}
}
Related
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();
}
}
this is the simplified plan for a solution:
for some reasons i need to run a windows form through a backgroundworker that is runnig by another backgroundworker, when the new windows form loads, the older backgroundworker must pause. i write the code like this :
creating a class with name : temp
public class temp
{
static public BackgroundWorker backgroundWorker1 = new BackgroundWorker() { WorkerSupportsCancellation = true };
static public EventWaitHandle ew = new EventWaitHandle(false, EventResetMode.ManualReset);
static public BackgroundWorker back = new BackgroundWorker() { WorkerSupportsCancellation = true };
}
the codes for form1 are :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
temp.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
temp.back.DoWork += new DoWorkEventHandler(back_DoWork);
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
Form2 f = new Form2();
f.Show();
}
private void button1_Click(object sender, EventArgs e)
{
temp.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
temp.back.RunWorkerAsync();
if (temp.backgroundWorker1.CancellationPending)
temp.ew.WaitOne();
}
}
}
and the codes of form2 goes here :
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
temp.backgroundWorker1.CancelAsync();
temp.ew.Reset();
}
}
}
by clicking the button1 from form1 the temp.backgroundworker1 runs and then in the DoWork of temp.backgroundworker1, the temp.back runs and then FORM2 LOADS BUT THE FORM2 HANGS AND BECOMES USELESS AND YOU CANNOT USE THAT ANY MORE.
where did i wrong ?
the whole plan that i'm going to execute is :
we have a For loop that processes every row of a DataGridView.
each time in a certain point, another windowsform opens
and it stops the loop until the user inserts the information and then click on OK button, the windowsform closes and the loop keep on working. i dont know what to do.......
even if i dont cancel working of the temp.backgroundworker in form2load like the code below, the Form2 is useless
private void Form2_Load(object sender, EventArgs e)
{
}
Do not use any UI operation in the work thread (DoWork method). Maybe that's why you set the CheckForIllegalCrossThreadCalls property, but your app will not work properly just suppresses the error when the debugger is attached.
See my answer here for the correct usage of the BackgroundWorker (that is about canceling but you can see the operations in UI and worker thread).
In this particular case what you can use a similar volatile bool to sign the UI thread that the form can be shown. Or, if you want to send different messages between the threads, use a ConcurrentQueue<T> to write and read messages:
public partial class Form1 : Form
{
private enum Message
{
ShowForm2,
SuspendWork,
ResumeWork,
FinishWorker1
// ... and whatever you want
}
private Timer timer;
private ConcurrentQueue<Message> messagesToUI = new ConcurrentQueue<Message>();
private ConcurrentQueue<Message> messagesToWorker = new ConcurrentQueue<Message>();
public Form1()
{
InitializeComponent();
timer = new Timer(this);
timer.Interval = 10;
timer.Tick += PollUIMessages;
timer.Enabled = true;
}
void PollUIMessages(object sender, EventArgs e)
{
// do we have a new message?
Message message;
if (messagesToUI.TryDequeue(out message))
{
switch (message)
{
case Message.ShowForm2:
Form2 f = new Form2();
f.Show();
// todo: in Form2.Close add a Resume message to the messagesToWorker
break;
// ... process other messages
}
}
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
// Here you are in the worker thread. You can send a message to the
// UI thread like this:
messagesToUI.Enqueue(Message.ShowForm2);
bool isWorking = true;
// and here you can poll the messages to the worker thread
while (true)
{
Message message;
if (!messagesToWorker.TryDequeue(out message))
{
// no message: idle or work
if (isWorking)
DoSomeWork(); // do whatever you want
else
Thread.CurrentThread.Sleep(10);
continue;
}
switch (message)
{
case Message.FinishWorker1:
// finishing the worker: jumping out
return;
case Message.SuspendWork:
isWorking = false;
break;
case Message.ResumeWork:
isWorking = true;
break;
}
}
}
I have main program
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Worker w1 = new Worker(1);
Worker w2 = new Worker(2);
Thread w1Thread = new Thread(new ThreadStart(w1.StartWorking));
Thread w2Thread = new Thread(new ThreadStart(w2.StartWorking));
w1Thread.Start();
w2Thread.Start();
Application.Run(new MainWindow());
if (w1Thread.IsAlive)
{
w1Thread.Abort();
}
if (w2Thread.IsAlive)
{
w2Thread.Abort();
}
}
}
and worker class:
class Worker
{
public int m_workerId;
public bool m_workerLifeBit;
public bool m_workerWork;
public Worker(int id)
{
m_workerId = id;
m_workerLifeBit = false;
}
public void StartWorking()
{
while (!m_workerWork)
{
m_workerLifeBit = false;
System.Threading.Thread.Sleep(5000);
m_workerLifeBit = true;
System.Threading.Thread.Sleep(5000);
}
}
}
I have checkBox on MainWindow form.
How to monitor state of Worker variable m_workerLifeBit and display its changes in MainWindow checkBox?
I have found this q&a How to update the GUI from another thread in C#? hovewer the answer does not show complete example, and I failed with using thread safe delegate.
I want some event mechanism that I fire in Worker.StartWorking and catch in slot in MainWindow form.
Here is a simple version using events:
class Worker
{
public event Action<bool> WorkerLifeBitChanged;
// ...
public void StartWorking()
{
// ...
m_workerLifeBit = false;
OnWorkerLifeBitChanged();
// ...
private void OnWorkerLifeBitChanged()
{
if (WorkerLifeBitChanged != null)
WorkerLifeBitChanged(m_workerLifeBit);
}
Then you wire up the event in Main:
//...
var mainWindow = new MainWindow();
w1.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w2.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w1Thread.Start();
w2Thread.Start();
Application.Run(mainWindow);
//...
And UpdateWorkerLifeBit implementation in MainWindow:
public void UpdateWorkerLifeBit(bool workerLifeBit)
{
if (this.checkBox.InvokeRequired)
{
this.Invoke(new Action(() => checkBox.Checked = workerLifeBit));
}
else
{
checkBox.Checked = workerLifeBit;
}
}
As mentioned in the comments, if this is a WinForms application then I'd recommend using a BackgroundWorker.
Kicking off the bg worker and subscribing to events:
BackgroundWorker worker = new BackgroundWorker();
// Subscribing to the worker method. Do all of your work here
worker.DoWork += worker_DoWork;
// Subscribing to the progress changed event where you'll want to update the UI
worker.ReportProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
// Subscribing to the worker completed event. Fires when the work is complete
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
// This line starts the worker
worker.RunWorkerAsync();
You would then have your methods defined as such:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Perform some work with the object you've passed in e.g.
MyObj foo = (MyObj)e.Argument;
foo.Name = "foobar";
// Notify UI
worker.ReportProgress(100, foo);
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Update UI
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Worker has finished
}
One solution would be passing a reference of your Program class (or even a delegate in your program class, or a data reference in your worker class) to the Worker thread. You can call a function of your Program directly from the thread code then. You can also use signals, but for this small example my previous "solution" is acceptable.
After implementing the changes to deadlocked C# Windows Form Application according to the article Lock Up Unlocked I am still having the same problem as in previous code of article Locked Up!
That is, upon clicking the button few times rapidly, the application hangs up (becomes unresponsive).
Why?
And how to correct it?
using System;
using System.Windows.Forms;
using System.Threading;
namespace LockupUnlocked
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_t = new Thread(new ParameterizedThreadStart(WorkerThread));
}
private Thread _t;
private object lockObject = new object();
private bool StopThread = false; ////changes added to avoid deadlock
private void WorkerThread(object sender)
{
Thread.Sleep(1000);
//while (true)
while (!StopThread)//changes added to avoid deadlock
{
string result = "This is a Test";
IAsyncResult aResult;////changes added to avoid deadlock
lock (lockObject)
{
Thread.Sleep(25);
//lblResult.Invoke(new MethodInvoker(delegate { lblResult.Text = result; }));
aResult = lblResult.BeginInvoke//changes to avoid deadlock
(new MethodInvoker(delegate { lblResult.Text = result; }));
}
lblResult.EndInvoke(aResult);//changes added to avoid deadlock
Thread.Sleep(500);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
StopThread = true;
}
private void Form1_Load(object sender, EventArgs e)
{
_t.Start();
}
private void button1_Click(object sender, EventArgs e)
{
lock (lockObject)//changes added to avoid deadlock
{
lblResult.Text = "Override the Test";
}
}
}
}
To me it looks like lock contention, not necessarily dead-locking.
You iterate a while loop every 25 milliseconds (not 25 seconds, that'd be 25000), clicking the button would then interrupt this asking for a lock, it may never be given that lock as the sleep is inside the lock scope.
Without clicking the button this may appear to work, however, the button click waiting on a lock will be blocking the UI thread, causing the "not responding" message to appear because the form doesn't have available time to paint itself.
You actually don't need to lock in order to update the text value. When you invoke from a Control, it simply pushes a message onto the UI message queue, which is processed synchronously on the UI thread. The worst you can do is a race condition, which won't corrupt any shared state.
Remove the locking and the code should still work as you expect.
I am hesitant to offer up any sample code as I don't know what you are trying to achieve with this sample.
The pattern to work with conflicting threads that need GUi access in Windows.Forms involves the InvokeRequired property and the Invoke function. Locking is not neccessary then.
namespace WindowsFormsApplication1
{
using System;
using System.Threading;
using System.Windows.Forms;
public partial class Form1 : Form
{
private Thread thread;
public Form1()
{
this.InitializeComponent();
this.thread = new Thread(this.WorkerThread);
}
private void WorkerThread(object sender)
{
Thread.Sleep(1000);
while (true)
{
Thread.Sleep(25);
this.SetText("from thread");
Thread.Sleep(500);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.thread.Abort();
}
private void Form1_Load(object sender, EventArgs e)
{
this.thread.Start();
}
/// <summary>
/// This is a callback for the SetText Method.
/// </summary>
/// <param name="text">The text.</param>
private delegate void SetTextCallback(string text);
/// <summary>
/// This sets a text.
/// It's thread safe, you can call this function from any thread.
/// If it's not called from the UI-thread, it will invoke itself
/// on the UI thread.
/// </summary>
/// <param name="text">The text.</param>
private void SetText(string text)
{
if (this.InvokeRequired)
{
this.Invoke(new SetTextCallback(this.SetText), text);
}
else
{
this.lblResult.Text = text;
}
}
private void Button1_Click(object sender, EventArgs e)
{
this.SetText("from button");
}
}
}
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;
}