Cancelling a BackgroundWorker that performs a DB call - c#

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.

Related

Progress bar while sql command execute using background worker

I'm trying to create a progress bar wich shown up when the calculate button clicked. And runs until the execute finishes the process. I'm using background worker for the operation. And the progress bar is just a simple marquee so if the process ends i want to torn false the visible property.
here is the initialize
backgroundworker1 = new System.ComponentModel.BackgroundWorker();
backgroundworker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
so first when the calculate button clicked:
private void buttonCalculate_Click(object sender, EventArgs e)
{
//execute the background worker
this.backgroundworker1.RunWorkerAsync();
while (this.backgroundworker1.IsBusy)
{
progressBar1.Visible=true;
Application.DoEvents();
}
}
Here is the backgroundworker1_DoWork event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
connection = new MySqlConnection(prop.connectionString);
string query = "Call telephelyi_2013()";
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand();
cmd = connection.CreateCommand();
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
}
and finaly when the operation comleeted it shuld be do this
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
MessageBox.Show("Számítás befejzve!");
}
But after all when i run the program and click on the calculate button the progress bar shown up and just moving never ends. And i also see that the computer is calculating for a while and after stops but i never get that end message!
whats wrong?
You need to just start the background worker and then let it go, rather than holding up the main thread by waiting on the worker. Using DoEvents is just a hack that is going to cause more problems than it'll solve unless you're very intimately familiar with what it does, how it works, and when you should use it.
Just have your button click do this:
this.backgroundworker1.RunWorkerAsync();
progressBar1.Visible=true;
And voila.

Running a BackgroundWorker continuously

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.

Close form after stopping background worker not working

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();
}

GUI not updating until end of event

I have a gui that needs to be updated from a hardware device attached through a dll file and a textbox. My problem is that gui is not updated until the end of the event and I need to show something pause and then show something else. The hack of Application.DoWork didn't change anything. Anyone have any suggestions? Everything I was reading used either invoke or DoEvents and neither seem to change the behavior.
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
App.myMainWindow.image1.Visibility = Visibility.Hidden;
System.Windows.Forms.Application.DoEvents();
}
Thread.Sleep(4000);
}
You make the GUI thread sleep, obviously the GUI cannot be updated when its thread sleeps. Create a seperate thread and use the Dispatcher to update UI-elements if you must, you can savely send that thread to sleep and your GUI will still respond.
Edit: System.Windows.Forms.Application.DoEvents(); you are sure about that WPF tag, aren't you?
Take a look at this web page for beginning http://www.c-sharpcorner.com/UploadFile/jieying/UsingProgressBarStatusBarandTimerControlsinVS.NET11282005021220AM/UsingProgressBarStatusBarandTimerControlsinVS.NET.aspx
I'd use a backgroundworker and start it in the click handler.
This way the GUI will continue to be available and progress can be displayed.
I figured it out using timers as vittore had suggested. This must be the way to do it since you cannot sleep the GUI thread during GUI event handlers.
public partial class MainWindow : Window
{
static Timer _timer;
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
this.image1.Visibility = Visibility.Hidden;
this.image2.Visibility = Visibility.Visible;
_timer = new Timer(2000);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true; // Enable it
}
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
textBox1.Dispatcher.Invoke(new Action(delegate()
{
_timer.Enabled = false;
this.image1.Visibility = Visibility.Visible;
this.image2.Visibility = Visibility.Hidden;
}));
}

Is CancelAsync working or not?

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;
}
}

Categories