I have been trying many different thing and can't get this code to work. My code to stop backgroundworker then close window.
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (bw.IsBusy)
{
bw.CancelAsync();
e.Cancel = true;
MessageBox.Show("close"); //Does show
return;
}
base.OnFormClosing(e);
}
During bw worker
if (worker.CancellationPending)
{
MessageBox.Show("Cancel"); // Does not show
//Cancel
e.Cancel = true;
}
On completed background worker
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Completed"); //Does not work
//Check if restart
if (bw_restart)
{
bw_restart = false;
bw.RunWorkerAsync();
}
//If it was cancelled
if (e.Cancelled)
{
this.Close();
}
//If error show error message
else if (e.Error != null)
{
MessageBox.Show(e.Error.ToString()); // Does not show
}
else //No errors or cancelled
{
MessageBox.Show(e.ToString()); //Does not shoiw
}
}
Cancel button
private void cancel_Click(object sender, EventArgs e)
{
bw.CancelAsync(); //Does not work :s
}
It does not close the window, the X when pressed does not do anything, I got it to close the form but not with stopping the background worker, driving me a bit mad. Link to code i got for this problem that not working: How to stop BackgroundWorker on Form's Closing event?
if (e.Cancelled)
That's fundamentally wrong. You can never be 100% sure that it will be set. Canceling a BGW is always a race condition, the BGW might have been busy exiting when you called its CancelAsync() method so never saw the CancellationPending set to true so never assigned e.Cancel = true in the DoWork event handler.
All you know for a fact is that mClosePending is reliable, since it was set to true on the UI thread. So always call Close() it it is set to true, regardless of the e.Cancelled state.
And yes, checking e.Error doesn't hurt either. But still check mClosePending.
As stated in my comment, your BackgroundWorker has ended due to an error, try adding the following at the top of your run worker completed. Once this error has been resolved your question will be more answerable.
if(e.Error != null)
MessageBox.Show(e.Error.toString());//Put a breakpoint here also
CancelAsync doesn't actually abort your thread or anything like that. It sends a message to the worker thread that work should be cancelled via BackgroundWorker.CancellationPending. Your DoWork delegate that is being ran in the background must periodically check this property and handle the cancellation itself.
Look at this:
private BackgroundWorker background;
private void Form1_Load(object sender, EventArgs e)
{
background = new BackgroundWorker();
background.WorkerSupportsCancellation = true;
background.DoWork += BackgroundOnDoWork;
background.RunWorkerCompleted += BackgroundOnRunWorkerCompleted;
background.RunWorkerAsync();
}
private void BackgroundOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
MessageBox.Show("stop");
}
private void BackgroundOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
// your doWork loop should check if someone don't call background.CancelAsync();
while (!background.CancellationPending)
{
// do something
}
}
private void ButtonClick(object sender, EventArgs e)
{
background.CancelAsync();
}
Related
Let's say I have a background worker like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//Kill zombies
}
}
How can I make this background worker start and stop using a button on a WinForm?
Maybe you can use a manualresetevent like this, I didn't debug this but worth a shot. If it works you won't be having the thread spin its wheels while it's waiting
ManualResetEvent run = new ManualResetEvent(true);
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(run.WaitOne())
{
//Kill zombies
}
}
private void War()
{
run.Set();
}
private void Peace()
{
run.Reset();
}
Use the CancelAsync method.
backgroundworker1.CancelAsync();
In your loop inside the worker thread.
if (backgroundWorker.CancellationPending) return;
This doesn't happen immediately.
This is how to do it (link to answer below)
By stop do you really mean stop or do you mean pause?
If you mean stop, then this is a piece of cake. Create a button click event handler for the button you want to be responsible for starting the background worker and a button click event handler for the one responsible for stopping it. On your start button, make a call to the background worker method that fires the do_work event. Something like this:
private void startButton_Click(System.Object sender,
System.EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
On your stop button, make a call to the method that sets the background worker's CancellationPending to true, like this:
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
}
Now don't forget to check for the CancelationPending flag inside your background worker's doWork. Something like this:
private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e)
{
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
}
And your doWork method:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
KillZombies(worker, e);
}
I hope this can steer you in the right direction. Some further readings:
http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.90).aspx
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
http://msdn.microsoft.com/en-us/library/waw3xexc.aspx
I haven't tested this, I have code somewhere that I'll have to see exactly what I did, but something like this is an adaptation of Fredrik's answer:
private bool _performKilling;
private object _lockObject = new object();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
if (_performKilling)
{
//Kill zombies
}
else
{ //We pause until we are woken up so as not to consume cycles
Monitor.Wait(_lockObject);
}
}
}
private void StartKilling()
{
_performKilling = true;
Monitor.Pulse(_lockObject);
}
private void StopAllThatKilling()
{
_performKilling = false;
]
More complete example of this pattern here:
https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs
I need to be able to continuously run my BackgroundWorker. The DoWork event contains a pool threaded process and the OnComplete updates my UI.
I have not been able to find a way to infinitely loop the BackgroundWorker.RunWorkerAsync() method without the whole program freezing. Any help would be greatly appreciated.
You have to make a loop in your DoWork-Method. To update your UI you shoud use the ProgressChanged-Method. Here is a small example how this can look like
public Test()
{
this.InitializeComponent();
BackgroundWorker backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += BackgroundWorkerOnDoWork;
backgroundWorker.ProgressChanged += BackgroundWorkerOnProgressChanged;
}
private void BackgroundWorkerOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
object userObject = e.UserState;
int percentage = e.ProgressPercentage;
}
private void BackgroundWorkerOnDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker) sender;
while (!worker.CancellationPending)
{
//Do your stuff here
worker.ReportProgress(0, "AN OBJECT TO PASS TO THE UI-THREAD");
}
}
I have done this in the past when needing something to run in the background.
If you try to run the backgroundworker while it is running, you will get an excpetion!
That is why i make the BackGroundWorker start itself when it is done in the completed event.
And then it will loop forever.
private void Main_Load(object sender, EventArgs e)
{
// Start Background Worker on load
bgWorker.RunWorkerAsync();
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000); // If you need to make a pause between runs
// Do work here
}
private void bgCheck_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Update UI
// Run again
bgWorker.RunWorkerAsync(); // This will make the BgWorker run again, and never runs before it is completed.
}
timer.interval=60000 // 1 min
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
try
{
//Do something
}
catch
{
}
}
on backgroundworker completed event, just start background worker again
If your program is freezing, it may be because your infinitely looping background worker thread is spinning, using 100% CPU. You haven't said why you need it to run in an infinite loop, but you could start by putting a Thread.Sleep in that loop.
I have a WPF application with a child window.Within that I'm doing a database connectivity check on a button click(in a background worker). The process take some time to complete and within that time if the user closes the child window by clicking on close button, the window closes but the background worker continues to run and displays the message after sometime.
Here is the sample code:
BackgroundWorker worker;
private void Button_Click(object sender, RoutedEventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
bsyInd.IsBusy = true;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
// checking database connectivity
string connstring=myconnstring;
SqlConnection con=new SqlConnection(connstring);
con.Open();
if(con.State==ConnectionState.Open)
e.Result=true;
}
catch (Exception)
{
e.Result=false;
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bool canConnect=(bool)e.Result;
if(canConnect)
MessageBox.Show("Connected");
else
MessageBox.Show("Failed");
bsyInd.IsBusy = false;
}
//close child window
private void ChildWindow_CloseButton_Click(object sender, RoutedEventArgs e)
{
//cancel the running process
worker.CancelAsync();
}
All the solutions I found online shows examples that continuosly monitor/poll the CancellationPending property of the background worker within a loop inside Do_Work. Here as the process i intend to do doesn't require any loops, how can I monitor the CancellationPending status on child window close button click event and cancel the background process?
Thanks in advance.
Provided that there is way to abort a blocking call to SqlConnection.Open, you could simply declare the SqlConnection object as a member variable in your class and execute the aborting call in the Close button handler.
private SqlConnection con;
private void ChildWindow_CloseButton_Click(object sender, RoutedEventArgs e)
{
if (con != null)
{
con.Dispose(); // or whatever would abort Open()
con = null;
}
}
In the worker thread Open would presumably throw kind of an Aborted exception, that you could catch and then set e.Cancel = true;.
Dennis, you could use an approach with AutoResetEvent, see AutoResetEvent Class
You signal to the thread on which the sql connection is happening that the resource can be released.
You are still using a separate thread so nothing should be blocked, but with the ability now to communicate between the threads and specially notify one of them to release the resources. Apologies if this is not in the format intended.
That link has an example of how to use the AutoResetEvent.
I've made a small app where Form is threaded (using BackgroundWorker), and in the form I'm calling a function QuitApplication in Program class when I want to quit.
The DoWork looks like this:
static void guiThread_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
if (Program.instance.form != null)
{
Program.instance.form.UpdateStatus(Program.instance.statusText, Program.instance.statusProgress);
}
Thread.Sleep(GUI_THREAD_UPDATE_TIME);
}
}
and in the Form1 class i have this method attached to the closing of the window:
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
Program.instance.SetStatus("Closing down...", 0);
Program.QuitApplication();
}
So what i want is to ensure that everything quits when I press the X on the window. However, the if( worker.CancellationPending == true ) never hits... why is this?
QuitApplication looks like this:
public static void QuitApplication()
{
Program.instance.guiThread.CancelAsync();
Application.Exit();
}
And Im using guiThread.WorkerSupportsCancellation = true
CancelAsync is setting the CancellationPending property, but then you immediately quit the application without giving the background thread a chance to detect that and shut down. You need to change your UI code to wait for the background thread to finish.
Personally, when I write apps like this, I make the form close button act like a Cancel button rather than quit immediately. It's a lot safer for the end user. For example:
private void abortButton_Click(object sender, EventArgs e) {
// I would normally prompt the user here for safety.
worker.CancelAsync();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
if(worker.IsBusy) {
// If we are still processing, it's not very friendly to suddenly abort without warning.
// Convert it into a polite request instead.
abortButton.PerformClick();
e.Cancel = true;
}
}
I'm working on a windows App in C#, I have a for-loop which update something in a loop, and I have 3 buttons on the form named "Stop,Pause,Resume". So the purpose is as same as the buttons named. Does anyone know how to do this?
Here is the Loop
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
setGeneralValue();
for (int i = 1; i <= autoGridView.Rows.Count - 1; i++)
{
if (SRP == "Pause") // this is what I was thinking but it won't work
{ // it will step into end-less loop
do // how to stop this loop on "Resume" button click
{
}while(SRP!="Resume")
}
car = false;
try
{
MemberID = Convert.ToInt64(autoGridView.Rows[0].Cells["Member_ID"].Value);
DispID = Convert.ToString(autoGridView.Rows[0].Cells["Disp_Id"].Value);
Mobile = Convert.ToString(autoGridView.Rows[0].Cells["Mobile"].Value);
DueDate = Convert.ToString(autoGridView.Rows[0].Cells["Due_Date"].Value);
}
catch (Exception)
{
MessageBox.Show("Row Not Found");
}
AutoRecharge(network_name, pack_name, Mobile, Mobile, Convert.ToString(autoGridView.Rows[0].Cells["Rck_Amt"].Value), vendor_id, vendor_pwd, pack_id, oxinetwork_id);
autoGridView.Rows.RemoveAt(0);
}
}
Here are the 3 button events in which I'm setting a variable
private void btnPause_Click(object sender, EventArgs e)
{
SRP = "Pause";
}
private void btnStop_Click(object sender, EventArgs e)
{
SRP = "Stop";
autoGridView.DataSource = "";
}
private void btnResume_Click(object sender, EventArgs e)
{
SRP = "Resume";
}
The reason this doesn't work as you expect is this:
A Windows Forms application uses a single UI thread, which continually processes incoming messages from a queue. Any event handlers you attach to the events of a Windows Forms control get sent to this queue and processed by the UI thread as quickly as possible.
Your btnCompleteAuto_Click is one such handler. Once it starts, nothing else will be processed by the UI thread until it exits. Thus any other handlers you attach to other events (btnPause_Click, btnStop_Click, etc.) must wait their turn, as they will run on the same (UI) thread.
If you want pause/resume functionality, this has to be achieved on a separate thread.
A possible way to implement it might be to use a BackgroundWorker, as suggested by saurabh.
Here is a rough sketch of what your updated code might look like (I have not even attempted to compile this, let alone debug it; it's intended only as a basic outline of how you might accomplish this functionality).
You need to be aware, however, that accessing UI controls directly from a non-UI thread is a no-no. Use a mechanism such as the BackgroundWorker.ProgressChanged event to handle any UI updates that you need to happen based on activity on a non-UI thread.
ManualResetEvent _busy = new ManualResetEvent(false);
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
{
_busy.Set();
btnAutoCompleteAuto.Text = "Pause";
backgroundWorker.RunWorkerAsync();
}
else
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Resume";
}
btnStop.Enabled = true;
}
private void btnStop_Click(object sender, EventArgs e)
{
_busy.Set();
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// for (something)
// {
_busy.WaitOne();
if (backgroundWorker.CancellationPending)
{
return;
}
// Do your work here.
// }
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Start";
btnStop.Enabled = false;
}
After Reading your actual requirement in our comment , i would suggest that use Background worker class which supports cancellation of running process.
See here