How to get out a while loop when in Task.Delay - c#

I am trying to stop a while loop in my program when an abort key is pressed, and the function running is running a Task.Delay. Unfortunately, even though this must be easy to do I just cannot get it to work for me. Please help.
I have a button that asks the user to confirm they want to run and if yes it comes to the function below and starts to run the RunSequence function. I did have this on a new thread but have now changed it to a Task, I leave the commented out code in just in case I need to run it instead of a task. RunSequence has two parameters the second is what I think I should have and that is a CancellationToken.
CancellationTokenSource tokenSource = new CancellationTokenSource();
private void ConfirmRunSequence()
{
//put it on a thread as the UI is slow to update
//var thread = new Thread(() => { RunSequence(_filePathName, tokenSource.Token); });
//thread.IsBackground = true;
//thread.Start();
Task.Run(() => RunSequence(_filePathName, tokenSource.Token), tokenSource.Token);
}
When the abort button is pressed, we set the Token to cancel and I want to drop out the While loop.
private void onAbort()
{
Abort = true; //set to abort sequence
tokenSource.Cancel();
}
I hopefully have the bits above correct, and I think the next bit is what I do not understand. Here I have a CancellationToken called _ct which I believe is tokenSource. My delay here is big so when I see the label update a few times I will then click to abort and it will be inside the delay which I want to cancel. Now this is what I cannot get to work.
I get a red sqiggly under _ct and it says “Cannot convert from System.Threading.CancellationToken to System.Threading.Task.Task”. Ok I read the words but sorry I do not know how to fix it but I also do not know if I did fix it if this is the correct way to get out the While loop, please help.
private async void RunSequence(string filePath, CancellationToken _ct)
{
Int count = 0;
while (!sr.EndOfStream)
{
lbl_count = count++;
await Task.WhenAny(Task.Delay(10000), _ct);
}
lbl_count =”aborted”;
}
Amongst the things I have tried is to change from await Task.WhenAny(Task.Delay(10000), _ct); to
Just Task.Delay(10000, _ct) but also no good.

Rather than using Task.Delay, you can access the WaitHandle of the CancellationToken and call WaitOne on that, passing a timeout.
The return value from the WaitOne operation will be true if the token was cancelled and false if the timeout was reached, from which you can then take appropriate further action.

I made a small app to show how I got my app to work. Create a small C# Winform .Net with two buttons one to run and one to abort and a label. As there was a request for the code I have included the full example program at Github
https://github.com/zizwiz/Cancellation_Token_Example
I also add a copy of some of the code below:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Cancel_Token_Example
{
public partial class Form1 : Form
{
CancellationTokenSource tokenSource; // Declare the cancellation token
public Form1()
{
InitializeComponent();
}
private void btn_run_Click(object sender, EventArgs e)
{
tokenSource = new CancellationTokenSource(); //Make a new instance
Task.Run(() => RunSequence(tokenSource.Token)); //Run the task that we need to stop
}
private void btn_abort_Click(object sender, EventArgs e)
{
tokenSource.Cancel(); // make the token a cancel token
}
private async void RunSequence(CancellationToken _ct)
{
int counter = 0;
while (!_ct.IsCancellationRequested)
{
// show incrementing number but as we have a task watch for cross threading
WriteUIData((counter++).ToString());
try
{
await Task.Delay(1000, _ct); //waits 1 second
}
catch
{
// Do nothing just needed so we can exit without exceptions
}
}
if (_ct.IsCancellationRequested)
{
//report we have cancelled
WriteUIData("Cancelled");
}
tokenSource.Dispose(); //dispose of the token so we can reuse
}
private void WriteUIData(String data)
{
// Write data to UI but as we have a task watch for cross threading
if (lbl_output.InvokeRequired)
{
lbl_output.BeginInvoke((MethodInvoker)delegate () { lbl_output.Text = data; });
}
else
{
lbl_output.Text = data;
}
}
}
}

Related

Cancelling an Async Task When TextBox Text Changed Then Restart It

I've .NET app and there are DataGridView and TextBox on my GUI. What I want to do is when user change TextBox text, update DataGridView where cells contains this text. But this search should run as async task because if it's not, it causes freeze on GUI. Everytime when user changed TextBox text, my app should cancel if another search task running and rerun it again to search according to new search values. Here is my code;
CancellationTokenSource cts = new CancellationTokenSource();
private async void TextBox1_Changed(object sender, EventArgs e)
{
cts.Cancel();
CancellationToken ct = cts.Token;
try
{
await Task.Run(() =>
{
System.Diagnostics.Debug.WriteLine("Task started");
// Searching here.
System.Diagnostics.Debug.WriteLine("Task finished");
}, cts.Token);
}
catch
{
System.Diagnostics.Debug.WriteLine("Cancelled");
}
}
On my code, tasks are canceled without it's started. I only see "Cancelled" line on debug console. I should cancel tasks because if I don't their numbers and app's CPU usage increases. Is there way to do that ?
Like Rand Random said, i should decleare new CancellationTokenSource object. I have edited my code like this and it's worked. Code should be like that:
CancellationTokenSource cts = new CancellationTokenSource();
private async void TextBox1_Changed(object sender, EventArgs e)
{
cts.Cancel();
cts.Dispose();
cts = new CancellationTokenSource();
try
{
await Task.Run(() =>
{
System.Diagnostics.Debug.WriteLine("Task started");
// Searching here.
System.Diagnostics.Debug.WriteLine("Task finished");
}, cts.Token);
}
catch
{
System.Diagnostics.Debug.WriteLine("Cancelled");
}
}
If you want to make life easy for yourself, you should consider using Microsoft's Reactive Framework (aka Rx)
I'm assuming you can write this method:
async Task<string[]> DoSearchAsync(string text, CancellationToken ct)
{
/* you implement this method */
}
Then you can do this:
private IDisposable _searchSubscription = null;
private void Form1_Load(object sender, EventArgs e)
{
_searchSubscription =
Observable
.FromEventPattern(h => TextBox1.TextChanged += h, h => TextBox1.TextChanged -= h)
.Throttle(TimeSpan.FromMilliseconds(400.0))
.Select(ep => TextBox1.Text)
.Select(text => Observable.FromAsync(ct => DoSearchAsync(text, ct)))
.Switch()
.ObserveOn(TextBox1)
.Subscribe(results => { /* update your UI */ });
}
Now this watches your TextChanged event, waits 400.0 milliseconds in case you type another character - there's no sense firing off a load of searches if the user is still typing - and then it calls your new DoSearchAsync method.
Then it does a Switch which effectively means cancel any in-flight searchs and start this new one.
Finally it marshalls back to the UI thread and then gives you the results of the search so that you can update the UI.
It just handles all of the calling, background thread calls, marshalling back to the UI, all for you.
If you want to shut it down, just call _searchSubscription.Dispose().
NuGet System.Reactive.Windows.Forms and add using System.Reactive.Linq; to get the bits.

Await task not working when triggered by code but if by user, it works

I have application for controlling LED strip. UI has combobox with effect selection and when user selects mode, it waits for currently running effect loop to finish by calling StopTask() and then executes selected effect. It sends LED color etc. to Arduino via serial. This works.
Problem is when I trigger StopTask() by MainWindow_OnClosing (when user exits the application), it triggers StopTask() but gets stuck on await currentEffectMode. I will try to explain it more by comments inside the code
MainWindow mode selection:
private void CbMode_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Checkbox selection triggers this
_ledStrip.LightModes.ChangeMode(CbMode.SelectedIndex);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
// Trigger disconnect and wait for success - this doesn't work (explained below in code comments)
_ledStrip.LightModes.Disconnect().Wait();
}
Light modes class:
private Task _modeTask;
private CancellationTokenSource _cancellationToken = new CancellationTokenSource();
// This is being triggered by change mode
internal async void ChangeMode(int mode)
{
// It waits for current loop to finish
await StopTask();
switch (mode)
{
case (int)Modes.Static:
// Then assigns new one
_modeTask = Static(_cancellationToken.Token);
break;
case (int)Modes.Breath:
_modeTask = Breath(_cancellationToken.Token);
break;
}
}
internal async Task StopTask()
{
if (_modeTask == null)
return;
// Set cancellation token to cancel
_cancellationToken.Cancel();
try
{
// and wait for task to finish. This works if triggered by user interaction BUT this is where it gets stuck when called by Disconnect() method (below). It awaits here forever
await _modeTask;
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// After sucessful await create new cts
_cancellationToken.Dispose();
_cancellationToken = new CancellationTokenSource();
}
}
// Example of LED effect loop
internal async Task Static(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
_ledStrip.FillLedsWithColor();
// Wait for strip to light up
await LightLeds();
// Delay before next loop round
await Task.Delay(15, cancellationToken);
}
}
// This is being called by window onclosing
internal async Task Disconnect()
{
//Stop current task and close serial connection. _device is serial
await StopTask();
Application.Current.Dispatcher.Invoke(() =>
{
if (_device.IsOpen())
{
_device.Clear();
_device.Close();
}
});
}
// Method for sending LED information to Arduino
internal async Task LightLeds()
{
if (!_device.IsOpen())
return;
await Task.Run(() =>
{
for (int i = 0; i < StaticValues.NumLeds; i++)
{
_device.Send((byte)i, _ledStrip.Leds[i].LedColor.R, _ledStrip.Leds[i].LedColor.G, _ledStrip.Leds[i].LedColor.B);
}
_device.LightUp();
});
}
I am beginner with Tasks and I am pretty sure that I am not using them properly (some of them are certainly unnecessary but I don't know it) and maybe it's the reason why it's not working. I tried to search and found many examples for using Tasks but I still don't understand it well.
Thank you!
Change MainWindow_OnClosing() to be async void instead, and use await Disconnect() instead of calling .Wait(). Event handlers are almost the only async methods for which this acceptable; the rest should have an async Task[<T>] signature instead. (There are some exceptions to the async part, but not the Task part, but I won’t muddy the waters with that here). This will stop you blocking (see link in Dmytro’s comment for more).
While there, change CbMode_OnSelectionChanged() to be similar (async void), make ChangeMode() async Task, and await it instead.
The only other minor thing of note is that if you move the device-closing code into your event handler instead (or refactor it into another method that you call from the event handler, after await Disconnect()), you should not need the Invoke(), as async event handlers - done correctly - give you this for free; i.e. effectively remaining on the UI thread while not blocking. (I’m assuming that’s what you trying to achieve there?)

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

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.

Infinite while loop with Form application C#

I need to run an infinite while loop when a form application starts. A form starts like this:
public Form1()
{
InitializeComponent();
}
Now I want to run another function which will have an infinite loop inside with one second sleep time:
public void doProcess(){
while(true){
Thread.Sleep(1000);
// other task
}
}
How can I do this? When I call doProcess() in the constructor, it does not show the form. I tried to run the while loop for 10 iterations. The form showed up only after all the iterations are finished. I don't understand why it is happening.
You can start a new thread like this:
new Thread(() =>
{
while (true)
{
Thread.Sleep(1000);
//other tasks
}
}).Start();
Although I suggest you read up on threading before you do. If you want to update the form from a different thread you should use: Form.Invoke().
For example: w is the form
w.Invoke((MethodInvoker) delegate
{
w.Width += 100;
});
In short, you are blocking the UI Thread with this infinite loop.
Run it async:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BeginWork();
}
private async void BeginWork()
{
while (true)
{
// Since we asynchronously wait, the UI thread is not blocked by the file download.
var result = await DoWork(formTextField.Text);
// Since we resume on the UI context, we can directly access UI elements.
formTextField.Text = result;
}
}
private async Task<string> DoWork(object text)
{
// Do actual work
await Task.Delay(1000);
// Return Actual Result
return DateTime.Now.Ticks.ToString();
}
}
A while(true) can be a bit excessive for an update loop. May I recommend that you potentially use a Timer, and/or leverage Cancellation Tokens to eagerly cancel requests which have taken too long as to not update UI with potentially stale results in high performance scenarios.
E.g.
public partial class Form1 : Form
{
private readonly Timer _sampleTimer;
public Form1()
{
InitializeComponent();
_sampleTimer = new Timer
{
Interval = 500 // 0.5 Seconds
};
_sampleTimer.Tick += DoWorkAndUpdateUIAsync;
}
private async void DoWorkAndUpdateUIAsync(object sender, EventArgs e)
{
// Since we asynchronously wait, the UI thread is not blocked by "the work".
var result = await DoWorkAsync();
// Since we resume on the UI context, we can directly access UI elements.
resultTextField.Text = result;
}
private async Task<string> DoWorkAsync()
{
await Task.Delay(1000); // Do actual work sampling usb async (not blocking ui)
return DateTime.Now.Ticks.ToString(); // Sample Result
}
private void startButton_Click(object sender, EventArgs e)
{
_sampleTimer.Start();
}
private void stopButton_Click(object sender, EventArgs e)
{
_sampleTimer.Stop();
}
}
It's happening because the ctor never exits and so the form cannot be shown - is this not obvious?
If you want to run a forever/sleep loop line this, you must thread it off.
Do not wait in GUI event handlers, (or ctors).
Can you not use forms.timer?
You are blocking the UI thread. Therefore, the UI cannot be processed as long as doProcess runs.
If you use .Net 4.5, you can use async waits:
public async void doProcess(){
while(true){
await Task.Delay(1000);
// other task
}
}
The cleaner solution would be to use a timer that fires an event every 1 second. You can turn off the timer after 10 loops.
You didn't exit the constructor so the form won't show.
If you want to do it after form shows place your code in Form_Load event.
But you rather want to do it using background thread so you can use backgroundworker
You could place it after the Initialize component, or find the load event of the form and paste your code in there

Categories

Resources