I have 2 functions I created which does specific works.
I call these functions on timers. But before function 1 completes, function 2 starts processing. How can I wait till 1 completes before starting 2.
Here is my code:
private void btnrun_Click(object sender, EventArgs e)
{
try
{
_bat.ShowDialog();
StrBatchNumber = _bat.GlobalBatchNumber;
DialogResult _diaresult = _bat.DialogResult;
if (_diaresult == DialogResult.OK)
{
if (StrBatchNumber == "")
{
MessageBox.Show("Please enter a batch number", "Batch Number Missing", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
btnstartrobo.PerformClick();
btnautostart.PerformClick();
}
}
}
catch(Exception)
{
}
}
System.Windows.Forms.Timer tmrfirst = new System.Windows.Forms.Timer();
private void btnautostart_Click(object sender, EventArgs e)
{
tmrfirst.Interval = 1000;
tmrfirst.Tick += new EventHandler(tmrfirst_Tick);
tmrfirst.Start();
}
private void tmrfirst_Tick(object sender, EventArgs e)
{
btnchecksignal.PerformClick();
Thread.Sleep(100);
if (textBox8.Text.Contains("+1"))
{
button14.PerformClick();
Thread.Sleep(20);
button42.PerformClick();
Thread.Sleep(300);
btnautostart.PerformClick();
}
}
I tried giving thread.sleep in between, but it doesn't work. How can I fix this? Please help.
The reason why your solution is not working correctly, is because both operations run in the same thread. Therefore your Thread.Sleep(100) is making both functions wait, not making the order change but delaying the complete program execution.
The easiest solution to solve this would be Multithreading (see the documentation on how to implement another thread here and how to start it running a function here)
Like this you can add the new Thread to the end of your first method or insert it into a helper method to run it only after method 1 has completed. If you call the same function several times, maybe check if the thread already exists before creating a new one or simply close it every time your second function is finished.
Related
This question already has answers here:
WinForm Application UI Hangs during Long-Running Operation
(3 answers)
Closed 5 years ago.
My program is suppose to perform tests on 8 electronic products of the same model simultaneously. The previous programmer has implemented some form of multi-threading in the program to accomplish this. However, when 5 slots or more are being tested, the UI becomes unresponsive and the results being written to a text file may get corrupted.
Below I will insert a pseudo-code on what's going on in the program.
private void button1_Click(object sender, EventArgs e)
{
//create_thread_1 <= mainFunction 1
//start thread 1
}
private void button2_Click(object sender, EventArgs e)
{
//create_thread_2 <= mainFunction 2
//start thread 2
}
private void button3_Click(object sender, EventArgs e)
{
//create_thread_3 <= mainFunction 3
//start thread 3
}
private void mainFunction1
{
//perform test A
//write test A result to textFile1 //calls writeToTextFile1
//perform test B
//write test B result to textFile1 //calls writeToTextFile1
//continues on and finishes all tests
//aborts thread1
//end
}
private void mainFunction2
{
//perform test A
//write test A result to textFile2 //calls writeToTextFile2
//perform test B
//write test B result to textFile2 //calls writeToTextFile2
//continues on and finishes all tests
//aborts thread2
//end
}
private void mainFunction3
{
//perform test A
//write test A result to textFile3 //calls writeToTextFile3
//perform test B
//write test B result to textFile3 //calls writeToTextFile3
//continues on and finishes all tests
//aborts thread3
//end
}
private void writeToTextFile1
{
//creates and saves results into textFile1
}
private void writeToTextFile2
{
//creates and saves results into textFile2
}
private void writeToTextFile3
{
//creates and saves results into textFile3
}
My theory is that only a single thread can open and write data into a text file at a single time, so when another thread have to write data, that thread has to wait and causes the UI to become unresponsive. Am I right here? If I'm not, any advice is greatly appreciated.
One of the solutions that I have read online is to declare the WriteToTextFile function as a new Thread so that other main threads can wait for each other without slowing down the UI. Is this the correct approach?
EDIT: added the important parts of the coding for better understanding..This code runs for one slot only but the other 9 slots basically uses the same code here
private void button1_Click(object sender, EventArgs e)
{
if (this.button1.Text == "START")
{
this.txtSerial1.ReadOnly = false;
this.txtSerial1.Select();
MessageBox.Show("SLOT 1: Scan the serial number and press ENTER", "3458A
Heat Rack", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
else if (System.Windows.Forms.DialogResult.OK == MessageBox.Show("SLOT 1: Are
you sure about stopping?", "3458A Heat Rack",
MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation))
{
this.call_main1.Abort();
this.sentry1.Close();
this.sentry1.Dispose();
MessageBox.Show("SLOT 1: Unit can be safely removed now", "3458A Heat
Rack", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
this.txtSerial1.Clear();
this.txtStart1.Clear();
this.txtStatus1.Clear();
this.info1.Clear();
this.button1.Text = "START";
this.button1.BackColor = this.startColour;
this.txtStatus1.BackColor = Control.DefaultBackColor;
}
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
int num;
int test_num = default(int);
double resultrelay = default(double);
if (e.KeyChar == '\r')
{
if (this.txtSerial1.Text.Length == 0)
{
this.txtSerial1.ReadOnly = true;
}
else if (this.txtSerial1.Text.Length >= 10)
{
try
{
this.sentry1 = new DirectIO(string.Concat("GPIB",
this.busNumber_Slot1, "::22::INSTR"));
this.terminal1 = new DirectIO(string.Concat("GPIB0::14::INSTR"));
num = 1;
}
catch (Exception exception)
{
num = 0;
}
if (num != 1)
{
MessageBox.Show("SLOT 1: DUT Not Present !!", "3458A Heat Rack",
MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
this.txtSerial1.Clear();
this.txtSerial1.Select();
this.txtSerial1.ReadOnly = true;
}
else
{
this.button1.Select();
this.button1.Text = "RUNNING";
this.button1.BackColor = this.runningColour;
this.txtSerial1.Text = this.txtSerial1.Text.ToUpper();
this.txtStart1.Text = DateTime.Now.ToString();
this.txtSerial1.ReadOnly = true;
string txtBox1_serial = this.txtSerial1.Text;
this.call_main1 = new Thread(() => this.main_Program_slot1(sentry1,
terminal1, txtBox1_serial, 1, test_num,
resultrelay));
this.call_main1.Start();
}
}
else
{
MessageBox.Show("SLOT 1: Unit Serial Number Is Incorrect!!", "3458A
Heat Rack", MessageBoxButtons.OK,
MessageBoxIcon.Asterisk);
this.txtSerial1.Clear();
this.txtSerial1.Select();
}
}
}
public void slot1(string test) //function to update GUI
{
if (!base.InvokeRequired)
{
this.info1.Text = test;
}
else
{
Form1.test1 updateTestType = new Form1.test1(this.slot1);
object[] objArray = new object[] { test };
base.Invoke(updateTestType, objArray);
}
}
private void write_TestResultDetails1(string serialnumber, double resultLatest1)
{
//update results into textfile
}
private void main_Program_slot1(DirectIO sentry1, DirectIO terminal1, string sernum, int slot_Number, int test_num, double resultrelay)
{
for (int i = 1; i <= loop_Count; i++)
{
slot1("TEST A");
//performs testA
//calls write_TestResultDetails1
slot1("TEST B");
//performs testB
//calls write_TestResultDetails1
}
}
Hope this coding can help you guys to understand my problem better..
PS: seems like changing to using BackGroundWorker instead of making my own threads will be a better choice for this kind of program.
Windows forms programming has a few gotchas and keeping a UI responsive is tough. To help you debug this issue, I recommend you name your thread in form load so you can easily find it in the debugger (double click your form in the designer to get form load then call System.Threading.Thread.CurrentThread.Name = "My UI Thread". Launch your application and then when the UI hangs, break in the debugger. You can then observe the stacktrace of the UI Thread to find out where it is working (and where you need to use a thread to keep the UI responsive.
My hunch is that you have either used a synchronisation primitive incorrectly waiting for an answer on a thread, or you have accidentally launched some work without a thread which is hanging the UI.
There is a control called a BackgroundWorker which can be used to do work on a thread easily and then report the progress back with an event safely. This cuts down on the synchronisation work you need to do which might be helpful.
Totally agree with the previous comments btw, please post your actual code, just redact the method names etc. as the most likely issue is that your psuedo code and your actual code don't match.
I/O intensive tasks are perfect for asynchronous actions, i.e. writeToTextFile1(), writeToTextFile2(), and writeToTextFile3() can all be executed on different threads.
in your solution, the error might be caused from the fact that you wrapped two/three I/O method calls inside one thread.
I suggest you adopt the following pattern.
take writeToTextFile1() for example, I would use async/await pattern to define this method:
private async Task writeToTextFile1Async(string resultValue)
{
await Task.Run(() => {
//create and saves results into textFile1
});
}
rewrite mainFunction1() as follows:
private async Task mainFunction1Async()
{
string resultA = "***";
await writeToTextFile1Async(resultA);
string resultB = "***";
await writeToTextFile1Async(resultB);
//perform other things
}
Call this function inside Button1 click event handler:
private async void button1_Click(object sender, EventArgs e)
{
await mainFunction1Async();
}
I have this code:
public void Blah(IWin32Window _this)
{
for (int i = 0; i < item_quantity; i++)
{
try { File.Delete(item[0, i]); }
catch (Exception ex)
{
if (MessageBox.Show(_this, String.Format("Error while accessing {0}\n{1}"
, item[0, i], ex.Message), "Error", MessageBoxButtons.RetryCancel
, MessageBoxIcon.Error) == DialogResult.Retry)
{ i--; }
}
}
}
...and this code in the main UI thread:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
AnotherClass.Blah(this);
}
When I execute this code, I get the unsafe cross-thread exception. What's the safe way to do this operation?
What's the safe way to do this operation?
There is no real safe way to do this. The message box pops out of nowhere, without any direct connection to a command that the user gave. One failure mode is that the user continues working with your UI, clicking the mouse or pressing the space bar. And your message box pops up a millisecond before he clicked the mouse or pressed a key. He'll never see the message.
So something was supposed to be done, it didn't get done and the user is completely unaware of it. Not a good thing. You'll need to doctor your UI so this condition can never occur. Clearly that will require that you do error reporting a different way than by using a temporary message box. Many possible alternatives of course, could be as simple as a Label that reports state. StatusStrip is good for this.
The actual exception is a bogus one. It is triggered by the built-in diagnostics that checks that code uses UI in a thread-safe way. The underlying winapi call is GetParent(), one of the very few user32 Windows functions that can safely be called, and used, from a worker thread. The only legitimate reason I know where using Control.CheckForIllegalCrossThreadCalls to work around the problem is okay. But fix the real problem instead.
I'm not condoning the design, but you can pass in the Form to Blah() and then Invoke() against the referenced form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
{
button1.Enabled = false;
backgroundWorker.RunWorkerAsync();
}
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
SomeClass AnotherClass = new SomeClass();
AnotherClass.Blah(this);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
MessageBox.Show("Done!");
}
}
public class SomeClass
{
public void Blah(Form frm)
{
int item_quantity = 5;
for (int i = 0; i < item_quantity; i++)
{
try
{
//File.Delete(item[0, i]);
Console.WriteLine("i = " + i.ToString());
throw new Exception("duh");
}
catch (Exception ex)
{
frm.Invoke(new Action(() =>
{
DialogResult result = MessageBox.Show(frm, String.Format("Error while accessing {0}\n{1}", "something", ex.Message), "Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
if (result == DialogResult.Retry)
{
i--;
}
}));
}
}
}
}
You are trying to do UI work on a background thread, hence the cross-thread exception. RunWorkerCompletedEventArgs has a property called Error that will hold any exception that gets thrown by the RunWorkerAsync delegate. Set up a handler for RunWorkerCompleted on your BackgroundWorker and check if the Error property has a value. If it does, prompt the MessageBox in the handler because you will be on the UI thread at that point. Call the BackgroundWorker's RunWorkerAsync method again on the DialogResult.Retry scenario.
(You will probably have to tweak your BackgroundWorker and AnotherClass.Blah to take in the value of i to prime your loop condition for that second call to your BackgroundWorker. The DoWorkEventArgs has a property called Argument that you can use to pass in that value.)
You need to execute UI code like this when calling it from another thread:
// must use invoke because the timer event is running on a separate thread
this.Invoke(new Action(() =>
{
MessageBox.Show("Message");
}));
I want to show an example application of semaphores for a specific problem for my homework.
I added 3 buttons to my C# form and I want to show that only one button at a specific time executes the bank account function in my code. When I click the three buttons in two seconds, the bank account function must run only one time. Because I have a Thread.Sleep(6000) for 6 seconds wait in bank account function. But three of my click runs by 6 second intervals consecutively. How can I change my code to run only one time when I press three buttons consecutively.
The Code is:
namespace semafor_form
{
public partial class Form1 : Form
{
Semaphore semafor=new Semaphore(1,1);
delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (this.textBox2.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox2.Text = text;
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void BankAccount()
{
semafor.WaitOne();
double a = Convert.ToDouble (textBox1.Text) + Convert.ToDouble (textBox2.Text);
Thread.Sleep(6000);
semafor.Release();
SetText(a.ToString());
}
private void btnATM_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(BankAccount));
t.Start();
}
private void btnCOUNTER_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(BankAccount));
t.Start();
}
private void btnINT_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(BankAccount));
t.Start();
}
}
}
I may have misread your question. You don't want the buttons to do anything when the thread is being used? (so you'll miss transactions?)
Try this:
private void BankAccount()
{
if (semafor.WaitOne(0))
{
double a = Convert.ToDouble (textBox1.Text) + Convert.ToDouble (textBox2.Text);
Thread.Sleep(6000);
semafor.Release();
SetText(a.ToString());
}
}
Try changing Semaphore semafor=new Semaphore(1,1); to Semaphore semafor=new Semaphore(0,1);
You are initializing a new semaphore without ever releasing it.
This really doesn't sound like an appropriate use of a semaphore. Your problem definition, if I'm reading it correctly, says that the three buttons are mutually exclusive: that pressing any one of them will render all of the buttons inactive for six seconds. You can use a semaphore for this, but a mutex would be more appropriate.
In any event, the problem you're experiencing is that you're waiting on the semaphore, so when the first transaction is done one of the other threads will acquire the semaphore and process. What you want to do is to try to acquire the semaphore. Here's an example.
An example of the latter:
private void BankAccount()
{
if (semafor.WaitOne(0)) // tries to acquire the semaphore
{
double a = Convert.ToDouble (textBox1.Text) + Convert.ToDouble (textBox2.Text);
Thread.Sleep(6000);
semafor.Release();
SetText(a.ToString());
}
}
The WaitOne(0) says, "Try to acquire the semaphore. If it's not immediately available, then return false. If it is available, acquire it and return true."
You could also do this in the button handler. That is, have the button handler acquire the semaphore (using WaitOne(0)), and have it exit without starting the thread if it can't acquire the semaphore. If it does acquire the semaphore, start the thread and have the thread proc release the semaphore when done.
How to use BackgroundWorker in C#?
Actually i'm performing an operation of filling a PDF-Form from method called fill(). It takes more time to show up the result into pdfviewer, so I decided to show up a 'processing image' using a backgroundworker, and tried using it but failing to achieve it
here is my code snippet :
private void bgwLoadFile_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke((MethodInvoker)delegate()
{
????
});
}
private void bgwLoadFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
}
else if (e.Error != null)
{
}
else
{
picLoading.SendToBack();
}
}
Fill method is called when button FILL is been clicked
private void btnFill_Click(object sender, EventArgs e)
{
if (btnFill.Text == "Fill")
{
bgwLoadFile.RunWorkerAsync();
picloading.BringToFront();
Fill();
}
wat statement should i need to add in DoWork method , if i tried to add FILL() fill is been called twice ...
can any one help me out
Thanks
Add Fill(); to your bgwLoadFile_DoWork and remove it from btnFill_Click
Just a side-note you'll probably want to call your picLoading.SendToBack(); outside of that 'else' as if you error or cancel it will stay there.
So let's try to find some answers:
The method worker_DoWork() will be executed within another thread. By calling within that method this.Invoke() you're going to pass the call back to the gui thread, which makes the usage of the background worker useless. Instead within the worker method you have to call the method that needs some time and doesn't interact with the gui. If this called method produces any result (e.g. has a return value) you should write this information into the variable e.Result.
The method worker_RunWorkerCompleted() will be called within the gui thread again. Allowing you to take the result and let it somehow interact with the gui. Due to the fact, that this method will be executed on the gui thread it should be quite simple (or fast) in its doing otherwise your gui is going to freeze again.
So given these informations lets clean up your code:
private void btnFill_Click(object sender, EventArgs e)
{
if (btnFill.Text == "Fill")
{
// Update the gui for the user
// and start our long running task
// (disable buttons etc, cause the
// user is still able to click them!).
picloading.BringToFront();
bgwLoadFile.RunWorkerAsync();
}
}
private void bgwLoadFile_DoWork(object sender, DoWorkEventArgs e)
{
// Let's call the long running task
// and wait for it's finish.
Fill();
}
private void bgwLoadFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// We're back in gui thread.
// So let us show some results to the user.
if (e.Cancelled)
{
// To support cancellation, the long running
// method has to check some kind of cancel
// flag (boolean field) to allow fast exit of it.
labelMessage.Text = "Operation was cancelled.";
}
else if (e.Error != null)
{
labelMessage.Text = e.Error.Message;
}
// Hide the picture to allow the user
// to access the gui again.
// (re-enable buttons again, etc.)
picLoading.SendToBack();
}
I have a form with 2 comboboxes on it. And I want to fill combobox2.DataSource based on combobox1.Text and combobox2.Text (I assume that the user has completed input in combobox1 and is in the middle of inputting in combobox2). So I have an event handler for combobox2 like this:
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
As far as building DataSource is time-consuming process (it creates a request to database and executes it) I decided that it's better to perform it in another process using BackgroundWorker. So there's a scenario when cmbDataSourceExtractor hasn't completed its work and the user types one more symbol. In this case I get an exception on this line
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues ); about that BackgroundWorker is busy and cannot perform several actions in the same time.
How to get rid of this exception?
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 run in the background must periodically check this property and handle the cancellation itself.
The tricky part is that your DoWork delegate is probably blocking, meaning that the work you do on your DataSource must complete before you can do anything else (like check for CancellationPending). You may need to move your actual work to yet another async delegate (or maybe better yet, submit the work to the ThreadPool), and have your main worker thread poll until this inner worker thread triggers a wait state, OR it detects CancellationPending.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx
If you add a loop between the CancelAsync() and the RunWorkerAsync() like so it will solve your problem
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
The while loop with the call to Application.DoEvents() will hault the execution of your new worker thread until the current one has properly cancelled, keep in mind you still need to handle the cancellation of your worker thread. With something like:
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
The Application.DoEvents() in the first code snippet will continue to process your GUI threads message queue so the even to cancel and update the cmbDataSourceExtractor.IsBusy property will still be processed (if you simply added a continue instead of Application.DoEvents() the loop would lock the GUI thread into a busy state and would not process the event to update the cmbDataSourceExtractor.IsBusy)
You will have to use a flag shared between the main thread and the BackgroundWorker, such as BackgroundWorker.CancellationPending. When you want the BackgroundWorker to exit, just set the flag using BackgroundWorker.CancelAsync().
MSDN has a sample: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancellationpending.aspx
MY example . DoWork is below:
DoLengthyWork();
//this is never executed
if(bgWorker.CancellationPending)
{
MessageBox.Show("Up to here? ...");
e.Cancel = true;
}
inside DoLenghtyWork :
public void DoLenghtyWork()
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
inside OtherStuff() :
public void OtherStuff()
{
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
What you want to do is modify both DoLenghtyWork and OtherStuff() so that they become:
public void DoLenghtyWork()
{
if(!bgWorker.CancellationPending)
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
public void OtherStuff()
{
if(!bgWorker.CancellationPending)
{
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
The problem is caused by the fact that cmbDataSourceExtractor.CancelAsync() is an asynchronous method, the Cancel operation has not yet completed when cmdDataSourceExtractor.RunWorkerAsync(...) exitst. You should wait for cmdDataSourceExtractor to complete before calling RunWorkerAsync again. How to do this is explained in this SO question.
My answer is a bit different because I've tried these methods but they didn't work. My code uses an extra class that checks for a Boolean flag in a public static class as the database values are read or where I prefer it just before an object is added to a List object or something as such. See the change in the code below. I added the ThreadWatcher.StopThread property. for this explation I'm nog going to reinstate the current thread because it's not your issue but that's as easy as setting the property to false before accessing the next thread...
private void combobox2_TextChanged(object sender, EventArgs e)
{
//Stop the thread here with this
ThreadWatcher.StopThread = true;//the rest of this thread will run normally after the database function has stopped.
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
all fine
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
Now add the following class
public static class ThreadWatcher
{
public static bool StopThread { get; set; }
}
and in your class where you read the database
List<SomeObject>list = new List<SomeObject>();
...
if (!reader.IsDbNull(0))
something = reader.getString(0);
someobject = new someobject(something);
if (ThreadWatcher.StopThread == true)
break;
list.Add(something);
...
don't forget to use a finally block to properly close your database connection etc. Hope this helps! Please mark me up if you find it helpful.
In my case, I had to pool database for payment confirmation to come in and then update WPF UI.
Mechanism that spins up all the processes:
public void Execute(object parameter)
{
try
{
var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, "transactionRef");
Process.Start(new ProcessStartInfo(url));
ViewModel.UpdateUiWhenDoneWithPayment = new BackgroundWorker {WorkerSupportsCancellation = true};
ViewModel.UpdateUiWhenDoneWithPayment.DoWork += ViewModel.updateUiWhenDoneWithPayment_DoWork;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerCompleted += ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerAsync();
}
catch (Exception e)
{
ViewModel.Log.Error("Failed to navigate to payments", e);
MessageBox.Show("Failed to navigate to payments");
}
}
Mechanism that does checking for completion:
private void updateUiWhenDoneWithPayment_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(30000);
while (string.IsNullOrEmpty(GetAuthToken()) && !((BackgroundWorker)sender).CancellationPending)
{
Thread.Sleep(5000);
}
//Plug in pooling mechanism
this.AuthCode = GetAuthToken();
}
Mechanism that cancels if window gets closed:
private void PaymentView_OnUnloaded(object sender, RoutedEventArgs e)
{
var context = DataContext as PaymentViewModel;
if (context.UpdateUiWhenDoneWithPayment != null && context.UpdateUiWhenDoneWithPayment.WorkerSupportsCancellation && context.UpdateUiWhenDoneWithPayment.IsBusy)
context.UpdateUiWhenDoneWithPayment.CancelAsync();
}
I agree with guys. But sometimes you have to add more things.
IE
1) Add this worker.WorkerSupportsCancellation = true;
2) Add to you class some method to do the following things
public void KillMe()
{
worker.CancelAsync();
worker.Dispose();
worker = null;
GC.Collect();
}
So before close your application your have to call this method.
3) Probably you can Dispose, null all variables and timers which are inside of the BackgroundWorker.