Background Worker won't receive CancellationPending in c# WinForm application - c#

I have a problem with backgroundworkers in my WinForm application.
Here is my scenario:
I have a background workers that starts in OnLoad form event. Then I have a checkbox on the form to stop/start the workers.
When I uncheck the box the event call the cancelAsync() method, but the worker don't receive CancellationPending.
To debug this problem I have tried to add a Button on the form that perform the same as the CheckedChanged event, in this case it works???!!!
This is a snippet of my code:
The workers ...
private void BwMB_DoWork(object sender, DoWorkEventArgs e)
{
bwMBExitEvent.Reset();
bool loop = true;
while (loop)
{
if (bwMB.CancellationPending)
{
loop = false;
}
... other code ...
}
e.Cancel = true;
bwMBExitEvent.Set();
}
The CheckedChanged event ...
private void checkBoxModBus_CheckedChanged(object sender, EventArgs e)
{
try
{
if (checkBoxModBus.Checked)
{
if (!bwMB.IsBusy)
bwMB.RunWorkerAsync();
}
else
{
if (bwMB.IsBusy)
{
bwMB.CancelAsync();
bwMBExitEvent.WaitOne();
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
And the button click event for debug ...
private void button2_Click(object sender, EventArgs e)
{
bwMB.CancelAsync();
bwMBExitEvent.WaitOne();
}
When I click on the button the worker receives the cancellation signals and exit loop setting the bwMBExitEvent (ManualResetEvent). In this way the click event WaitOne end waiting.
When I uncheck the box the worker, stop running, but don't receive the signal, so don't ends the loop and the event is not set. The CheckedChanged's WaitOne never ends.
Please excuse any English grammar or spelling issues.

First of all, BGW is obsolete, fully replaced by async/await, Tasks and Progress<T>. Tasks allow composition, continuations and cancellation, something that's quite complex with BGWs. I suspect the bwMBExitEvent event is used to implement a continuation after a BGW finishes.
The article Async in 4.5: Enabling Progress and Cancellation in Async APIs explains how cancellation and progress reporting work in .NET 4.5 and later (ie all supported versions).
That said, BGW has no problem with cancellation. I suspet the event,loop variables and other unseedn code end up causing race conditions.
Using 2, 4 or 10 cancellable tasks instead of BGW's though is easy.
Multiple tasks can be started easily with Task.Run.
It's possible to await multiple tasks to finish without blocking with Task.WhenAll.
Cancellation can be signaled to threads, tasks on asynchronous operations with CancellationTokenSource
Starting multiple tasks is easy :
private void StartTasks()
{
_cts=new CancellationTokenSource();
//Start each method passing a CancellationToken
_tasks=new[]{
Task.Run(()=>WorkerMethod1(_cts.Token)),
Task.Run(()=>WorkerMethod2(_cts.Token)),
...
};
//Enable the Cancel button
Cancel.Enabled=true;
}
This code create N tasks and stores them in an array. It also creates a new CancellationTokenSource that can be used for signalling cancellation to all tasks or threads monitorint its tokens
To cancel the tasks with a button call CancellationTokenSource.Cancel() and await for all tasks to complete :
private async void Cancel_Clicked(object sender,EventArgs args)
{
if (_cts!=null)
{
lblStatus.Text = "Cancelling";
//Signal a cancellation
_cts.Cancel();
//Asynchronously wait for all tasks to finish
await Task.WhenAll(_tasks);
_cts=null;
lblStatus.Text = "Cancelled";
}
//Disable the button
Cancel.Enabled=false;
}
By using async/await the handler isn't blocking while waiting for the tasks to finish. It doesn't need Invoke or BeginInvoke either, as execution resumes on the UI thread after await.
All the worker methods have to do is check the CancellationToken.IsCancellationRequested flag :
private void WorkerMethod1(CancellationToken token)
{
//If cancellation isn't requested
while(!token.IsCancellationRequested)
{
//Loop one more time
}
}
Putting everything together :
//Hold active tasks
Task[] _tasks;
private void WorkerMethod1(CancellationToken token)
{
//If cancellation isn't requested
while(!token.IsCancellationRequested)
{
//Loop one more time
}
}
CancellationTokenSource _cts;
private void OnLoad(...)
{
//Fire the tasks
StartTasks();
}
private void StartTasks()
{
_cts=new CancellationTokenSource();
//Start each method passing a CancellationToken
_tasks=new[]{
Task.Run(()=>WorkerMethod1(_cts.Token)),
Task.Run(()=>WorkerMethod2(_cts.Token)),
...
};
//Enable the Cancel button
Cancel.Enabled=true;
}
private async void Cancel_Clicked(object sender,EventArgs args)
{
if (_cts!=null)
{
//Signal a cancellation
_cts.Cancel();
//Asynchronously wait for all tasks to finish
await Task.WhenAll(_tasks);
_cts=null;
}
//Disable the button
Cancel.Enabled=false;
}

Related

EventWaitHandle waitOne() causes my worker thread to suspend until it has timed out

I am using a EventWaitHandle() handler. This handler is called in a ButtonClick event that waits for 10 seconds. There is another worker thread which upon receiving some data call Set() on the handler.
Problem is the WaitOne() returns false after the timeout occurs. The worker thread doesnt run and looks like its suspended, hence Set() is not called. Once the timeout is over, my worker thread resumes and Set() method is called.
To verify I tried without the EventWaitHandle() to check if my worker thread actually takes 10 seconds of time, but it didnt, and Set() method had hit immediately.
I am not sure why the worker thread runs after the timeout has occurred in the
I am new to C#. Thanks in advance
MainWindow.xaml.cs
public static EventWaitHandle autoResetEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
XYZDialogBox.cs
private void BtnConnect_Click(object sender, RoutedEventArgs e)
{
MainWindow.autoResetEvent.Reset();
if (!MainWindow.autoResetEvent.WaitOne(10000))
{
//Line number details is not received from Service
MessageBox.Show("Timeout");
//now disconnect and exit
strCommand = "#cmddisconnect " + tbIPAddress.Text + " #";
tcpClient.AddCommandAsync(strCommand);
return;
}
}
ABC.cs
public void ABC(ref string strData)
{
while(strData != "")
{
//do something
MainWindow.autoResetEvent.Set();
}
}
Using the async await pattern based on your code:
private async void BtnConnect_Click(object sender, RoutedEventArgs e)
{
// await frees up the main thread but halts execution of this method until finished.
var result = await Task.Run(() =>
{
//Do something that takes time.
// This is now in another thread.
return MyService.ServiceCall(arg);
});
// use result here synchronously.
}
As an aside the example here is very rudimentary case of using the async await pattern. Ideally your service would have a library that implements an async method call that returns an awaitable task. That would look something like this.
private async void BtnConnect_Click(object sender, RoutedEventArgs e)
{
bool success = await MyService.ServiceCallAsync(args); // async service call.
if(success)
{
// handle
return;
}
// an error occurred.
}

How to stop multiple long recursive async tasks on WinForms C#

I am trying to stop multiple recursive async tasks fired by an initial Parallel.Foreach loop by using CancellationToken in my WinForms C# program, the program works nice without lagging the GUI but once I click the stop button the program becomes really laggish.
With small number of tasks it works okay-ish but when the number of concurrent tasks are high in number (more than 20 let's say) it doesn't stop some tasks properly or doesn't close some of them at all.
When this bug happens and I click the start button (button1) again where there is the main Parallel.Foreach loop, for some reason it creates a smaller number of tasks.
This way I am forced to close and reopen the program.
Is there a better way to stop multiple recursive async tasks?
This is my code and what I have tried so far:
private async void button1_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
await Task.Run(() => Parallel.ForEach(Array, async s =>
{
try
{
if (!cts.Token.IsCancellationRequested)
{
await LaunchMethod(cts.Token);
}
}
catch (System.OperationCanceledException)
{
Console.WriteLine("Aborting task");
}
}));
}
And this is the recursive method:
private async Task LaunchMethod(CancellationToken ct)
{
//Really long CPU and NETWORK intensive method here
//Throwing Cancellation Request periodically during the long method like this
ct.ThrowIfCancellationRequested();
//Then calling the recursive method and passing the token
try
{
if (counter < NumberOfLoops)
{
counter++;
await LaunchMethod(ct)
}
}
catch (System.OperationCanceledException)
{
Console.WriteLine("Aborting task");
}
}
And this is the stop button
private void CancelRequest()
{
if (cts != null)
{
cts.Cancel();
cts.Dispose();
}
}

C# Error creating window handle on Environment.Exit call

I've created a simple thread controller class managing the thread's execution, here its code:
public class ThreadController {
int waitCount;
Thread myThread;
public ThreadController() {
//
}
public void StartThread() {
waitCount = 0;
// launch a thread to show an alert when conditions are met!
myThread = new Thread(new ThreadStart(ThreadAction));
myThread.IsBackground = true;
myThread.Start();
}
// method is async as it call an async method itself!
void ThreadAction() {
while (myThread.IsAlive) {
Thread.Sleep(5000);
bool doStop = DoStopTest().Result; // some async function testing stop criterion
if (doStop) {
MainForm.BeginInvoke(new MethodInvoker(delegate() {
MessageBox.Show("Thread stopped!");
}));
//
myThread.Abort();
}
++waitCount;
if (waitCount >= 15) {
myThread.Abort();
}
Thread.Sleep(5000);
}
}
}
Now, I want to make sure the above created threads (there might be several) are killed when I close the MainForm, which I read should be done in the FormClosing event as follows:
void Main_FormClosing(object Sender, FormClosingEventArgs e) {
// unfortunately, an error is thrown when I call following line...
Environment.Exit(Environment.ExitCode);
}
The Environment.Exit call actually generates some weird exceptions... Sometimes a "vhost32.exe stopped working", sometimes an error System.ComponentModel.Win32Exception (0x80004005): Error creating window handle or other painting events that use "Invalid Parameters"...
Am I missing something here? What is the suggested way to cleanly close the form with all associated threads, without running into errors?
The code would be a lot clearer if you used tasks and async/await. DoStopTest() seems to return a Task already, so there's no need to use a raw Thread.
The code could be something as simple as a loop :
public async Task MyTestAndWait()
{
await Task.Delay(5000);
var waitCount=0;
while( waitCount++ < 15 && !(await DoStopTest()))
{
await Task.Delay(10000);
}
MessageBox.Show("Thread stopped!");
}
After each call to await execution resumes on the original synchronization context. For desktop applications, that's the UI thread. That means there's no need to use BeginInvoke
Threads should not be aborted. The correct way is to check a thread-safe signal, like a ManualResetEvent that's raised when a thread needs to exit. When signalled, the thread's code itself should exit.
Using a lot of events can get a bit messy which is why .NET 4.5 added the CancellationToken and CancellationTokenSource classes that can be used to notify both threads and Tasks they need to cancel and exit gracefully.
public async Task MyTestAndWait(CancellationToken ct,int initialDelay,int pollDelay)
{
await Task.Delay(initialDelay,ct);
var waitCount=0;
while(!ct.IsCancellationRequested && waitCount++ < 15 && !(await DoStopTest()))
{
await Task.Delay(pollDelay,ct);
}
MessageBox.Show("Poll stopped!");
}
This will cancel the delays and the loop but it won't cancel the call to DoStepTest(). That method will have to accept a CancellationToken parameter as well
CancellationTokens are created by CancellationTokenSource classes. One of the overloads accepts a timeout, which could be used to cancel the overall operation :
public async void SendSMS_Click(object sender, EventArgs args)
{
var cts=new CancellationTokenSource(TimeSpan.FromMinutes(15));
await MyTestAndAwait(cts.Token,5000,10000);
}
The cts could be stored in a field, to allow cancellation due to another event like a button click :
CancellationTokenSource _cts;
public async void SendSMS_Click(object sender, EventArgs args)
{
SendSMS.Enabled=false;
Cancel.Enabled=true;
_cts=new CancellationTokenSource(TimeSpan.FromMinutes(15);
await MyTestAndAwait(cts.Token,5000,10000);
_cts=null;
SendSMS.Enabled=true;
Cancel.Enabled=false;
}
public async void Cancel_Click(object sender, EventArgs args)
{
_cts?.Cancel();
}
The same code can be used to signal cancellation when closing the form :
void Main_FormClosing(object Sender, FormClosingEventArgs e)
{
_cts.?Cancel();
}
BTW there's no reason to call Environment.Exit() in the form's Closing or Closed events. Closing the main form will end the application unless there's another thread running.
UPDATE
It looks like the actual question is how to verify that an SMS was sent by polling for its send status. The code in this case would be different, while still using task. The method shouldn't have any reference to the UI so it can be moved to a separate Service-layer class. After all, changing providers shouldn't result in changing UIs
Assuming HttpClient is used, it could look like this :
//In an SmsService class
public async Task<(bool ok,string msg)> SendSmsAsync(string phone,string message,CancellationToken ct)
{
var smsMsg=BuildSmsContent(phone,string);
await _httpClient.PostAsync(smsMsg,ct);
//wait before polling
await Task.Delay(_initialDelay,ct);
for(int i=0;i<15 && !ct.IsCancellationRequested;i++)
{
var checkMsg=CheckStatusContent(phone,string);
var response=await _httpClient.GetAsync(check,ct);
if (ct.IsCancellationRequested) break;
//Somehow check the response. Assume it has a flag and a Reason
var status=ParseTheResponse(response);
switch(status.Status)
{
case Status.OK:
return (ok:true,"Sent");
case Status.Error:
return (ok:failed,status.Reason);
case Status.Pending:
await Task.Delay(_pollDelay,ct);
break;
}
}
return (ok:false,"Exceeded retries or cancelled");
}
This method could be used from a button event :
CancellationTokenSource _cts;
public async void SendSMS_Click(object sender, EventArgs args)
{
DisableSending();
var phone=txtPhone.Text;
var message=txtMessage.Text;
_cts=new CancellationTokenSource(TimeSpan.FromMinutes(15);
var (ok,reason)=await _smsService.SendSmsAsync(phone,message,cts.Token);
_cts=null;
if (ok)
{
MessageBox.Show("OK");
}
else
{
MessageBox.Show($"Failed: {reason}");
}
EnableSending();
}
public void EnableSending()
{
SendSMS.Enabled=true;
Cancel.Enabled=false;
}
public void DisableSending()
{
SendSMS.Enabled=false;
Cancel.Enabled=true;
}

How to close a form (used from a background thread) in thread safe manner?

I have a form that shows a data grid. I also have a method running on a different thread that updates only the displayed cells of the grid. To do this, this method calls a function on the form that returns the displayed cells.
The problem I have is that sometimes while the form has been closed and disposed the method on the other thread is still calling this function which results in an objectdisposed exception. Is there a way (other then making sure the methode on the other thread is finished) to prevent this?
So I need a thread safe method to kill the background task when the form is closed.
private delegate List<foo> GetShownCellsDelegate();
public List<foo> GetShownCells()
{
if (this.InvokeRequired)
{
GetShownCellsDelegate getShownCellsDelegate = new GetShownCellsDelegate(GetShownCells);
return (List<foo>)this.Invoke(getShownCellsDelegate);
}
else
{
//do stuff
}
}
I tries using the IsDisposed property of the form:
if (!IsDisposed)
{
return (List<foo>)this.Invoke(getShownCellsDelegate);
}
But apparently the form can be dispossed after the if statement because I still get the isdisposed exception.
This is how I use the function on the other thread:
private CancellationTokenSource cts = new CancellationTokenSource();
public void CancelUpdate()
{
cts.Cancel();
}
public void ReadDataFromDevice()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ReadAllDataThreadPoolMethod));
}
private void ReadAllDataThreadPoolMethod(Object stateInfo)
{
if (!cts.IsCancellationRequested)
{
//do stuff
}
}
The CancelUpdate method is called from the IsClosing event on the form. But I still get the isdisposed exception sometimes.
To cancel the long running operation you can use a CancellationToken, which is specifically designed for cooperative cancellation.
Have the main form create a CancellationTokenSource when starting the background thread, pass the CacellationToken generated by the CTS to the backround thread, cancel the CTS when your form closes, and then have the background thread check the token to see if it is cancelled before trying to invoke back to the main thread.
public void Foo()
{
var cts = new CancellationTokenSource();
var task = Task.Run(() => DoWork(cts.Token));
FormClosing += (s, args) =>
{
cts.Cancel();
if (!task.IsCompleted)
{
args.Cancel = true;
task.ContinueWith(t => Close());
}
};
}
private void DoWork(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Do some work
}
}
To be absolutely sure that the background thread doesn't pass the cancellation check, then yield to the UI thread to have it cancel the token and dispose of the form, before the work is done, you'll also need to ensure that the background thread has time to run to completion after being cancelled, before the form closes. This can be done through a simple Thread.Join call in the closing handler.
this.FormClosed += new FormClosedEventHandler(form1_FormClosed);
void form1_FormClosed(object sender, FormClosedEventArgs e)
{
//close thread
}
This will be executed whenever your form is being closed.

Assynchronous Cancelation while operation is running

I'm starting a few tests about asynchronous programing in .net and now i'm stuck at trying ti cancel a long operation using cancellationToken.
So I have the following code:
CancellationTokenSource cancelationToken = new CancellationTokenSource();
My buttons to start the operations
private void button2_Click(object sender, EventArgs e)
{
cancelationToken.Cancel(true);
}
private void button1_Click(object sender, EventArgs e)
{
StartOperation(cancelationToken.Token);
}
And finally my operations
private async void StartOperation(CancellationToken cancelToken)
{
await GetItensFromDatabase2(cancelToken);
}
public static Task<int> GetItensFromDatabase(CancellationToken cancelToken)
{
//cancelToken.Register( () => Console.WriteLine("Canceled") );
return Task.Factory.StartNew<int>(() =>
{
int result = 0;
cancelToken.ThrowIfCancellationRequested();
result = MyLongOperation(); // Simulates my operation -> I want to cancel while this operation is still running
return result;
}, cancelToken);
}
So, how to cancel MyLongOperation() method ? Is it possible to do ?
It is not possible to cancel in any point, the purpose of CancellationToken is to allow user to cancel the operation, when the long running operation expect that...
while(!finished)
{
cancelToken.ThrowIfCancellationRequested();
//Some not cancelable operations
}
Here is more common method of cancelable method
private static void LongRunning(CancellationToken cancelToken)
{
while (true)
{
if(cancelToken.IsCancellationRequested)
{
return;
}
//Not canceled, continue to work
}
}
The idea is, that user requests cancellation, but only executor decides when to stop his work. Usually executor do cancellation after reaching some "safe-point"
It is not good experiance to Abort long running operations without asking oppinion, a lot of posts have been written about this.
Well for starters you would have an issue that if you have already run through the logic which says "cancelToken.ThrowIfCancellationRequested" and then you cancel. How will the "MyLongOperation" know that you have cancelled the task? :)
Cancelling a long running task usually takes in a CancellationToken as an argument so the code would look something like:
// Check if cancelled
// do work part 1
// Check if cancelled
// do work part 2
The granularity of the cancellable operations is upto the developer.

Categories

Resources