Terminating Background Worker from a different class - c#

This is how I set up my Background Worker
The question is: How do I terminate the process midway? I have to create a cancel button that will stop current processing.
I thought of use the line below...
if ((worker.CancellationPending == true))
{
e.Cancel = true;
}
...and encapsulating the AllocateTrades2 method inside, but that would mean I have to call the Cancel Flag from somewhere else, and it will check it only after the method is done processing.
And I can't just "pretend" that it is not doing anything because if the user clicks the same operation again, an exception will be thrown that the worker is in use.
An example of what I want to do:
public void bw_DoWork(object sender, DoWorkEventArgs e)
{
AllocateTrades2();
}
public void AllocateTrades()
{
Method1();
CheckTerminate();
Method2();
CheckTerminate();
//Click the cancel button in the UI during method 3 processing
Method3();
CheckTerminate();
}
And the CheckTerminate() stops all of its processing and makes itself available for future calls. Is what I'm trying to do possible?

You'd have to do something like...
public void AllocateTrades()
{
if (!worker.CancellationPending)
{
Method1();
}
if (!worker.CancellationPending)
{
Method2();
}
if (!worker.CancellationPending)
{
Method3();
}
}
.Net cannot decide for you what is an "atomic" operation with respect to your background work, so you must decide how often and where to check for a pending cancellation.

The answer is YES.
Wherever you check for 'CancellationPending', you can stop your worker.
In your example you have these options:
check in the 'AllocateTrades2()'-Function itself
If (bw.CancellationPending) {
e.Cancel = True
return; }
Note: If you check it here, you can cancel the process 'midway'.
check from outside, before you run your next process
If (!worker.CancellationPending) {
MethodX(); }
Generally, you should check for CancellationPending wherever it makes sense (e.g. in a loop: after or before an item has been processed).

Related

Confused about backgroundworker not stopping when expected

I have the following code. It is just a form app. On load it will run the bacground worker.
Then I have a button that is supposed to stop the infinite loop in the background worker by setting a flag to true.
I'm logging the out put of the backgroundworker1.IsBusy and it says it is busy but according to the logic in my code it shouldn't be busy because I set the flag to true thus exiting the while loop and running the backgroundworker_Completed event.
I must be doing something wrong but I can not figure it out.
If I'm approaching this incorrectly could somebody either help me fix what I'm doing wrong or point me in a better direction on how I can accomplish what I"m trying to do here.
private volatile bool StopScanning = false;
private void myForm_Load(object sender, EventArgs e)
{
try
{
if (backgroundWorker1.IsBusy)
{
//do nothing
}
else
{
backgroundWorker1.RunWorkerAsync();
}
}
catch (Exception boo)
{
Log.log(boo.ToString());
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (StopScanning == false)
{
Application.DoEvents();
try
{
ReturnScannedItems();
System.Threading.Thread.Sleep(1000);
}
catch (Exception boo)
{
Log.log(boo.ToString());
}
}
}
private void cancelbutton_Click(object sender, EventArgs e)
{
try
{
Log.log("Setting Stop Scan flag to true");
StopScanning = true;
Log.log(CloseScanSession().ToString());
}
catch (Exception boo)
{
Log.log("Setting Stop Scan flag to true");
StopScanning = true;
Log.log(CloseScanSession().ToString());
Log.log(boo.ToString());
}
while (backgroundWorker1.IsBusy)
{
Log.log("Still busy");
}
this.Close();
}
You are blocking the UI thread, which prevents the BackgroundWorker from completing. It can't raise the RunWorkerCompleted event until the UI thread is free to process new messages (raising the event involves posting a message to the UI thread's message queue, so that the UI thread can then execute the code that will actually raise the event).
Your code also is flawed in that it's calling Application.DoEvents() from the worker thread. You should never call this method anyway, but it's particularly foolish to call it from a worker thread, because the whole point of having a worker thread is to avoid having to call that method (and it won't do anything when called on the worker thread anyway, because the worker thread shouldn't own any window objects that would need to receive a window message).
Instead of sitting in a busy loop, checking IsBusy and blocking the UI thread, you should just subscribe to the RunWorkerCompleted event and do whatever you need to do there. Without a good Minimal, Complete, and Verifiable code example that fully illustrates what you're actually trying to do, it's not possible to provide any more specific advice than that.

Could the Cancel flag suffer from race-condition when stoping and restarting a BGWorker?

I've seen this answer
Yes, the BackgroundWorker class sets the CancellationPending property to false before raising the RunWorkerCompleted event.
and the documentation of CancelAsync and RunWorkerAsync
I have this code that prevents InvalidOperationException being thrown. It's my code, so WorkerSupportsCancellation is true.
public void Start()
{
lock (OnOffLock)
{
if (worker.IsBusy)
return;
worker.RunWorkerAsync();
}
}
public void Stop()
{
lock (OnOffLock)
{
worker.CancelAsync();
}
}
I know that there is no way to avoid a posible race-condition happening if I call for CancelAsync while the worker is already stoping (for whatever reason). What I want to know for sure is something that it may be common sense, but it is not explained on the documentation.
When calling RunWorkerAsync (e.g. restarting the worker after an stop) it is CancellationPending set to false to avoid an unhandled cancel?
My guess is that when calling RunWorkerAsync, the CancellationPending flag is set to false no matter what was its previous value, but I didn't found confirmation on this.
According to this it's correct : BackgroundWorker.cs
public void RunWorkerAsync(object argument)
{
if (isRunning)
{
throw new InvalidOperationException(SR.GetString(SR.BackgroundWorker_WorkerAlreadyRunning));
}
isRunning = true;
cancellationPending = false;
asyncOperation = AsyncOperationManager.CreateOperation(null);
threadStart.BeginInvoke(argument,
null,
null);
}
Yes, there is very strong race potential. It can take a while for your RunWorkerCompleted event handler to start running, it depends on what your UI thread is doing. It could be long done and you have no way to find out that it is done from the UI thread. The reason that BGW resets CancellationPending, it doesn't tell you anything about what really happened.
You have to code this carefully so you can detect that it actually got cancelled. Roughly:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
(some-loop-construct) {
if (backgroundWorker1.CancellationPending) {
e.Cancel = true; // Important!
return;
}
// etc...
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
// Something bad happened
}
else if (e.Cancelled) {
// It actually got cancelled
}
else {
// It actually completed
}
}
Setting e.Cancel to true in your DoWork event handler is important, that's what sets e.Cancelled in your RunWorkerCompleted event handler

Is there anyway I can break from an infinite loop with a "hotkey" so to say?

I am writing this program that I want to run forever via a while loop and whenever the user presses a certain key on their keyboard it exits the program. I've looked everywhere but I have only seen KeyEvents, but the WindowsForm isn't active while the program is running. Anyone have a solution for me?
Edit: The program takes over the cursor so activating an event on the UI is basically impossible
Edit Two:
public void MainMethod()
{
while (true)
{
if (checkBox1.Checked == true) state = State.PERFORM_ACTION_ONE;
if (checkBox2.Checked == true) state = State.PERFORM_ACTION_TWO;
// More stuff checking which state to assign
switch (state)
{
case State.PERFORM_ACTION_ONE:
DoSomething();
break;
// More cases
// I want it to be able to break anywhere in the while loop
}
}
}
You need to set a HotKey like here Set global hotkeys using C# then using that HotKey to exit the application.
You need to run your infinite loop in a separate thread from the UI thread. And have a infinite loop to check on the variable that can be set from UI thread:
while (keepRunning){
// do stuff
}
and then set event on a button press to change keepRunning to false.
Here is the quick sample:
public partial class Form1 : Form
{
public static bool KeepRunning;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
KeepRunning = true;
Task.Factory.StartNew(() =>
{
while (KeepRunning)
{
Trace.WriteLine("Keep running");
}
});
}
private void button2_Click(object sender, EventArgs e)
{
KeepRunning = false;
Trace.WriteLine("Finished Execution");
}
}
if you start the loop in another thread you could cancel that thread that it is running when you hit the "hot key" or whatever you want to stop it. Check out BackgroundWorker.
Put the loop into a separate Task.
WinForms will continue to run concurrently on the UI thread, so it can continue to receive the user input. When user requests you to stop, you can use the task cancellation mechanism1 to exit from the loop and the task itself.
1 See the "Canceling a Task" section here.

Background Worker's IsBusy property is not reliable

I have placed a check in the system
public void ProcesssMessages(ResponseEventArgs message)
{
_queue.Enqueue(message);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}
Still it gives me error backgroundworker1 is already running and you can't run it twice. This is the only point in my screen where I run the background worker so there is no way someone in the meantime run it from other section.
Is there someway to make it safe or fail-proof.
I am not handling the completed event and thus there could be no way I am blocking that event.
I cant run it in completed event as this is in a user control and someone access a method in this user control and I add that message in a queue. I then need to process that message using background worker, but first need to check if background worker is already processing the queue. Code for the background worker is
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (_queue.Count > 0)
{
ResponseEventArgs currentMessage = _queue.Dequeue();
if (currentMessage != null)
{
AddUpdateDataRow(currentMessage);
UpdateMainUI();
}
}
}
Code for the UpdateMainUI method
private void UpdateMainUI()
{
if (gridControl1.InvokeRequired)
{
gridControl1.Invoke(new MethodInvoker(UpdateMainUI));
}
gridControl1.RefreshDataSource();
}
Thanks

Alternative to Thread.Suspend() method

Thread.Suspend() method is obsolete as you know. I want to suspend thread immidiately when button click event comes. I used Thread.Suspend() and it works perfect but everyone suggest that using Thread.Suspend() method is not a good method to suspend the task. I used a flag to suspend the task but every time when a button click event comes, i have to wait to exit from task. I used Thread.IsAlive flag to wait the thread to exit but this method freezes form.
void ButtonClickEvent(object sender, ButtonClickEventArgs e)
{
TheadExitFlag = false;
if(MyThread != null)
{
while(MyThread.IsAlive);
//MyThread.Suspend();
}
}
void MyTask(void)
{
while(TheadExitFlag)
{
// some process
Thread.Sleep(5000);
}
}
How can i suspend the thread immidiately?
There is no alternative with the same functionality, AFAIK. I am not sure if the problems with an OS Suspend() could be worked around within the language/libraries, but no attempt has been made to do so, so maybe it's too difficult or even sensibly impossible.
Until such an alernative exists, you are reduced to polling for a suspend flag and then waiting on some synchronization object for a 'resume' signal. I have used AutoResetEvent for this.
Use a ManualResetEvent to toggle between a running and idle state.
ManualResetEvent run = new ManualResetEvent(true);
void ResumeButton_Click(object sender, ButtonClickEventArgs e)
{
run.Set();
PauseButton.Enabled = true;
ResumeButton.Enabled = false;
}
void PauseButton_Click(object sender, ButtonClickEventArgs e)
{
run.Reset();
PauseButton.Enabled = false;
ResumeButton.Enabled = true;
}
void MyTask(void)
{
while (run.WaitOne()) // Wait for the run signal.
{
// Do work here.
}
}
There is an approximate solution to this problem at https://stackoverflow.com/a/45786529/3013473 using AspectJ and wait/notify.

Categories