In this answer the code is designed to have a delay between executions on a async task.
When I implement this I have a method that will cancel the token stopping the loop. I also wait on the task so that it is finished before I continue. If I don't catch the OperationCanceledException I will wait forever, but with it it works great.
I also tried replacing the while(true) with while (!cts.IsCancellationRequested) but it didn't stop the waiting.
Am I misunderstanding the usage?
private int count = 0;
private CancellationTokenSource cts;
private Task task;
public void ContinuousWorkTask(int interval)
{
cts = new CancellationTokenSource();
// Run the import task as a async task
task = Task.Run(async () => // <- marked async
{
try {
while (true) {
DoWork();
await Task.Delay(interval, cts.Token); // <- await with cancellation
}
}
catch (OperationCanceledException) { }
}, cts.Token);
}
private void DoWork()
{
count++;
}
public void Shutdown()
{
cts.Cancel();
task.Wait(); //Without the try/catch we never stop waiting
}
Update #1: Show a very simple DoWork() to illustrate that it is not the reason the task "hangs" without the try/catch block.
Update #2: I like to wait on the task because I often have some sort of clean up to do on the way out such as writing a result to disk and I want to be sure it is done. I may do this as part of an IDisposable Dispose() method.
Update #3: It has been pointed out that the task.Wait() will receive an AggregateException. I think that adds support that the task should be catching the OperationCancelledException internally for a clean shutdown as I show in my example.
If it shouldn't I'd like to see an answer that explains why it is a better idea that I should be catching it from within the Shutdown() code. I think it is a code smell, but I may be missing a design point.
Related
Either I am doing something really wrong, but the below never returns it hangs forever on the ReceiveAsync despite specifying a 1 second timeout.
I would expect it to return null value, after the time out.
/* snipped MyContainer class */
private readonly BufferBlock<byte[]> queue = new BufferBlock<byte[]>();
public async Task ExecuteAsync(CancellationToken stoppingToken)
{
// makes no difference if creating with TaskCreationOptions.LongRunning
await Task
.Factory
.StartNew(async () =>
{
while (stoppingToken.IsCancellationRequested == false)
{
// we get here OK, but no further if i use TimeSpan for delay
// without Timespan i.e. ReceiveAsync() only, it does **not** hang
var item = await
this
.queue
.ReceiveAsync(TimeSpan.FromMilliseconds(1000));
// process it, but we never get here we sleep forever
await ProcessAsync(item);
}
} /*,TaskCreationOptions.LongRunning*/);
// we get here and print the below OK
Console.WriteLine("thread created and running");
}
// this is called by the original (or some other) thread
// either if i call this or not, the above thread code still locks on ReceiveAsync
public void Add(byte[] data)
{
Console.WriteLine("adding");
this.queue.Post(data);
Console.WriteLine("done"); // gets posted OK
}
Important update - works OK if I do not specify a delay
var item = await this.queue.ReceiveAsync());
The code works OK if I remove the delay, however I do some background housekeeping every second (for packet counters etc) so this is important to wake up if nothing received within 1 second.
Other notes:
I am calling the above code from a generic dot net worker host:
public class Worker : BackgroundService
{
private readonly MyContainer containerClass;
private readonly ILogger<Worker> logger;
public Worker(MyContainer containerClass, ILogger<Worker> logger)
{
this.containerClass = containerClass;
this.logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
this.containerClass.ExecuteAsync(stoppingToken);
while (!stoppingToken.IsCancellationRequested)
{
this.logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
The above is called after the worker is built by IHostBuilder and I called Host.Run().
My understanding is (which I clearly need to work on!) since I create the thread, it should run totally independently from (and not block on) the thread that created/called it... in other words it should be able to call ReceiveAsync within the thread itself without getting blocked.
Using the Task.Factory.StartNew with an async delegate creates a nested task:
Task<Task> nestedTask = Task.Factory.StartNew(async () => { //...
You are awaiting the outer task, but not the inner, so the inner task becomes a fire-and-forget task. It is possible to await both tasks in one line by using the await operator twice:
await await Task.Factory.StartNew(async () => { //...
Alternatively you can combine the two tasks in one by using the Unwrap method.
await Task.Factory.StartNew(async () => { /* ... */ }).Unwrap();
...or even better use the Task.Run method instead of the Task.Factory.StartNew, because the former understands async delegates, and does the unwrapping for you:
await Task.Run(async () => { //...
If you are interested about the differences between Task.Factory.StartNew and Task.Run, you could read an informative article here.
Thanks everyone who responded and finally to Enrico (feel free to copy/paste and i will assign the answer to you) the code was actually running OK.
A TimeoutException exception was being thrown, but wasn't caught by my code or Visual Studio.
Enabling all CLR exceptions as per https://learn.microsoft.com/en-us/visualstudio/debugger/managing-exceptions-with-the-debugger?view=vs-2019 the exception started being thrown.
I then handled the exception within code, and was able to proceed as my design required:
public Task ExecuteAsync(CancellationToken stoppingToken)
{
return Task
.Factory
.StartNew(async () => {
while (stoppingToken.IsCancellationRequested == false)
{
try
{
var ts = TimeSpan.FromSeconds(UpdateFrequencySeconds);
var item = await this.queue.ReceiveAsync(ts);
await ProcessAsync(item);
}
catch (TimeoutException)
{
// this is ok, timer expired
}
catch (Exception e)
{
this.logger.LogError(e.ToString());
}
UpdateCounters();
}
await StopAsync();
},
stoppingToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default)
.Unwrap();
}
I have a Windows Service that monitors my application by running a couple of tests every second. A bug report has been submitted that said that the service stoppes running after a while, and I'm trying to figure out why.
I suspect that the code below is the culprit, but I have trouble understanding exactly how it works. The ContinueWith statement has recently been commented out, but I dont know if it is needed
private Task CreateTask(Action action)
{
var ct = _cts.Token;
return Task.Run(async () =>
{
ct.ThrowIfCancellationRequested();
var sw = new Stopwatch();
while (true)
{
sw.Restart();
action();
if (ct.IsCancellationRequested)
{
_logger.Debug("Cancellation requested");
break;
}
var wait = _settings.loopStepFrequency - sw.ElapsedMilliseconds;
if (wait <= 0) // No need to delay
continue;
// If ContinueWith is needed wrap this in an ugly try/catch
// handling the exception
await Task.Delay(
(int)(_settings.loopStepFrequency - sw.ElapsedMilliseconds),
ct); //.ContinueWith(tsk => { }, ct);
}
_logger.Debug("Task was cancelled");
}, _cts.Token);
}
Are there any obvious problems with this code?
Are there any obvious problems with this code?
The one that jumps out to me is the calculation for the number of milliseconds to delay. Specifically, there's no floor. If action() takes an unusually long time, then the task could fail in a possibly unexpected way.
There are several ways for the task to complete in either a cancelled or failed state, or it can delay forever:
The task can be cancelled before the delegate begins, due to the cancellation token passed to Task.Run.
The task can be cancelled by the ThrowIfCancellationRequested call.
The task can complete successfully after being cancelled, due to the IsCancellationRequested logic.
The task can be cancelled by the cancellation token passed to Task.Delay.
The task may fail with an ArgumentOutOfRangeException if _settings.loopStepFrequency - sw.ElapsedMilliseconds is less than -1. This is probably a bug.
The task may delay indefinitely (until cancelled) if _settings.loopStepFrequency - sw.ElapsedMilliseconds happens to be exactly -1. This is probably a bug.
To fix this code, I recommend two things:
The code is probably intending to do await Task.Delay((int) wait, ct); instead of await Task.Delay((int)(_settings.loopStepFrequency - sw.ElapsedMilliseconds), ct);. This will remove the last two conditions above.
Choose one method of cancellation. The standard pattern to express cancellation is via OperationCanceledExcpetion; this is the pattern used by ThrowIfCancellationRequested and by Task.Delay. The IsCancellationRequested check is using a different pattern; it will successfully complete the task on cancellation, instead of cancelling it.
There are so many problems with this code, that makes more sense to rewrite it than attempt to fix it. Here is a possible way to rewrite this method, with some (possibly superfluous) argument validation added:
private Task CreateTask(Action action)
{
if (action == null) throw new ArgumentNullException(nameof(action));
var ct = _cts.Token;
var delayMsec = _settings.loopStepFrequency;
if (delayMsec <= 0) throw new ArgumentOutOfRangeException("loopStepFrequency");
return Task.Run(async () =>
{
while (true)
{
var delayTask = Task.Delay(delayMsec, ct);
action();
await delayTask;
}
}, ct);
}
The responsibility for logging a possible exception/cancellation belongs now to the caller of the method, that (hopefully) awaits the created task.
var task = CreateTask(TheAction);
try
{
await task; // If the caller is async
//task.GetAwaiter().GetResult(); // If the caller is sync
_logger.Info("The task completed successfully");
}
catch (OperationCanceledException)
{
_logger.Info("The task was canceled");
}
catch (Exception ex)
{
_logger.Error("The task failed", ex);
}
Assume ClassX has a forever while loop.
Is there a way to "force stop/delete/clear memory" of a ClassX instance?
or do I have to stop the while loop manually within the class for GC?
The bulk of your question seems to be asking how to exit out of a continuous loop in an asynchronous context. The solution to your problem is cancellation tokens.
A simple example might be the following:
class Caller
{
private CancellationTokenSource _cancellationTokenSource;
private Runner _runner;
public async Task StartAsync()
{
_cancellationTokenSource = new CancellationTokenSource();
try
{
_runner = new Runner();
await _runner.DoWorkAsync(_cancellationTokenSource.Token);
}
catch (OperationCanceledException) when (_cancellationTokenSource.IsCancellationRequested)
{
// this will ignore the exception when cancellation is requested. it might be useful in a reset scenario.
// otherwise, don't try/catch and just let the exception bubble up
}
finally
{
_runner = null;
_cancellationTokenSource = null;
}
}
public void Stop()
{
_cancellationTokenSource?.Cancel();
}
}
class Runner
{
public async Task DoWorkAsync(CancellationToken cancellationToken)
{
while(true)
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
}
}
}
The caller's StartAsync() will create a CancellationTokenSource, and start the DoWorkAsync() method of the runner by passing in the cancellation token.
The caller's DoWorkAsync() method will, on every iteration of the loop, ask the token to throw an exception if the source has requested cancellation. Note that cancellation is cooperative - you have to check if cancellation has been requested for it to work. You should also pass the cancellationToken into any async methods that accept it, as I've demonstrated with Task.Delay.
Note that I've used catch (OperationCanceledException) when (_cancellationTokenSource.IsCancellationRequested) in the StartAsync() method. Since this is a reset scenario, I don't want the exception to bubble up and crash the application, but I don't want to catch similar exceptions caused by something other than our cancellation request, hence the when .... part. How you want to handle these things depends on you, I just figured I'd demonstrate this at the same time in case it's useful in your scenario.
Now, when we call Stop(), it simply flags the cancellation request on the token, so that our co-operative cancellation code will know it's time to cancel the execution.
Environment
Windows 7
Visual Studio
C#
What I'm trying to do
I'm trying to build an app to evaluate company products. For security, the description below is made abstract to some extent.
What this app does is changing a certain parameter in the product and see how a certain value of the product changes. So I need to do two things.
Change the parameter at a certain interval
Display the value in a textbox at a certain interval
The diagram is like this.
These tasks should be repeated until a cancel button is pressed.
The UI has these controls:
button1 : start button
button2 : cancel button
textbox1 : to show values obtained from the device
So here is the code I wrote.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cts = new CancellationTokenSource();
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
private async Task Task1()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
private void ChangeParameter(double param)
{
// change device paremeter
Console.WriteLine("devicep parameter changed : " + param);
}
private async Task Task2()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(100);
int data = GetDataFromDevice();
UpdateTextBoxWithData(data);
}
cts.Token.ThrowIfCancellationRequested();
}
private int GetDataFromDevice()
{
//pseudo code
var rnd = new Random();
return rnd.Next(100);
}
private void UpdateTextBoxWithData(int data)
{
textBox1.AppendText(data.ToString() + "\n");
// debug
Console.WriteLine("data : " + data);
}
private void button2_Click(object sender, EventArgs e)
{
cts.Cancel();
}
}
Issues
However, there are two issues in this code.
UI freezes.
Task2 is never executed.
The second issue is derived from await since it executes tasks one by one. I could have used Task.Run() but this doesn't allow adding values to textBox since it's different from the UI thread.
How can I solve these issues? Any help would be appreciated.
First of all, async methods can be illusive as they won't turn your methods magically asynchronous. Instead, you can consider an async method as a setup for a state machine (see a detailed explanation here), where you schedule the chain of operations by the await calls.
For that reason, your async methods must execute as fast as possible. Do not do any blocking operation in such a setup method. If you have a blocking operation, which you want to execute in the async method, schedule it by an await Task.Run(() => MyLongOperation()); call.
So for example this will return immediately:
private async Task Task1()
{
await Task.Run(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
}
A small remark: others may suggest to use Task.Delay instead of Thread.Sleep. I would say that use Task.Delay only if it is the part of the configuration of your state machine. But if the delay is intended to be used as a part of the long-lasting operation, which you don't want to split up, you can simply stay at the Thread.Sleep.
Finally, a remark for this part:
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
This configures your tasks to be executed after each other. If you want to execute them parallel, do it like this:
private async void button1_Click(object sender, EventArgs e)
{
Task t1 = Task1();
Task t2 = Task2();
await Task.WhenAll(new[] { t1, t2 });
}
Edit: An extra note for long-lasting tasks: By default, Task.Run executes the tasks on pool threads. Scheduling too many parallel and long lasting tasks might cause starvation and the whole application may freeze for long seconds. So for long-lasting operation you might want to use Task.Factory.StartNew with TaskCreationOptions.LongRunning option instead of Task.Run.
// await Task.Run(() => LooongOperation(), token);
await Task.Factory.StartNew(() => LooongOperation(), token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
The problem is you not using await in your tasks so they executing synchronously.
You should use something like this to maintain your UI responsive (NOTE this is not production code, I'm just showing an idea):
private void button1_Click(object sender, EventArgs e)
{
try
{
await Task.WhenAll(Task1(cts.Token), Task2(cts.Token));
}
catch (TaskCancelledException ex)
{
}
}
private async Task Task1(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500, token); // pass token to ensure delay canceled exactly when cancel is pressed
ChangeParameter(0);
await Task.Delay(1000, token);
ChangeParameter(10);
await Task.Delay(500, token);
ChangeParameter(0);
}
}
private async Task Task2(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(100, token);
int data = await Task.Run(() => GetDataFromDevice()); //assuming this could be long running operation it shouldn't be on ui thread
UpdateTextBoxWithData(data);
}
}
Basically, when you need to run something on background you should wrap that in Task.Run() and then await for result. Simply adding async to your method won't make this method asynchronous.
To make your code clearer, I suggest you to move methods like GetDataFromDevice or ChangeParameter to services layer. Also, take a look at IProgress as comments suggests to update your UI according to progress of some process.
There are many issues with this code:
async/await doesn't make the code asynchronous automagically. It allows you to await the results of already asynchronous operations. If you want to run something in the background that isn't already asynchronous, you need to use Task.Run or a similar method to start a Task.
await returns execution to the original synchronization context. In this case, the UI thread. By using Thread.Sleep, you are freezing the UI thread
You can't update the UI from another thread and that goes for Tasks too. You can use the IProgress interface though to report progress. A lot of BCL classes use this interface, just like CancellationToken
Maxim Kosov already cleaned up the code and shows how to properly use async/await and Task.Run, so I'll just post how to use IProgress< T> and its impelementation, Progress< T>
IProgress is used to publich a progress update with the IProgress< T>.Report method. Its default implementation, Progress, raises the ProgressChanged event and/or calls the Action<T> passed to its constructor, on the UI thread. Specifically, on the synchronization context captured when the class was created.
You can create a progress object in your constructor or your button click event, eg
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<int>(data=>UpdateTextBoxWithData(data));
//...
//Allow for cancellation of the task itself
var token=cts.Token;
await Task.Run(()=>MeasureInBackground(token,progress),token);
}
private async Task MeasureInBackground(CancellationToken token,IProgress<int> progress)
{
while (!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(data);
}
}
Note that using Thread.Sleep inside a task is not a good idea because it wastes a threadpool thread doing nothing. It's better to use await Task.Delay() which requires that the signature of the method change to async Task. There is a Task.Run(Func) overload just for this purpose.
The method is a bit different from Maxim Kosov's code to show that IProgress really communicates across threads. IProgress can handle complex classes, so you could return both a progress percentage and a message, eg:
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
Here I'm just being lazy and return a Tuple<int,string>. A specialized progress class would be more appropriate in production code.
The advantage of using an Action is that you don't need to manage event handlers and the objects are local to the async method. Cleanup is performed by .NET itself.
If your device API provides truly asynchronous calls, you don't need Task.Run. This means that you don't have to waste a Task in a tigh loop, eg:
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100, token);
int data = await GetDataFromDeviceAsync();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
Most drivers perform IO tasks using an OS feature called completion ports, essentially callbacks that are called when the driver completes an operation. This way they don't need to block while waiting for a network, database or file system response.
EDIT
In the last example, Task.Run is no longer needed. Just using await would be enough:
await MeasureInBackground(token,progress);
I have a task that needs to run periodically. My first implementation was like:
public static void CheckTask(CancellationTokenSource tokenSource)
{
do
{
// Do some processing
Console.WriteLine("Processing");
// Sleep awhile and wait for cancellation
// If not cancelled, repeat
} while (!tokenSource.Token.WaitHandle.WaitOne(1500));
Console.WriteLine("Bye bye");
}
This task is started like so:
CancellationTokenSource tokenSource = new CancellationTokenSource();
Task task = null;
task = new Task((x)=> {
CheckTask(tokenSource);
//CheckTask2(t, (object)tokenSource);
}, tokenSource.Token);
task.Start();
Then I thought instead of looping in the task, why not reschedule it using ContinueWith? My next implementation was like this:
public static void CheckTask2(Task task, object objParam)
{
CancellationTokenSource tokenSource = (CancellationTokenSource)objParam;
// Do some processing
Console.WriteLine("Processing");
// Sleep awhile and wait for cancellation
if(tokenSource.Token.WaitHandle.WaitOne(1500))
{
Console.WriteLine("Cancel requested");
return;
}
// Reschedule
task.ContinueWith(CheckTask2, tokenSource);
}
The second implementation is much easier to read and write and my tests showed no difference but I still wonder if there are drawbacks for a task to ContinueWith itself?
I still wonder if there are drawbacks for a task to ContinueWith
itself?
Frankly, i find your code less readable with the continuation attached (but that is only flavor based). The only drawback i see is the fact that you use a WaitHandle on the token which forces you now to dispose your CancellationToken object:
Accessing this property causes a WaitHandle to be instantiated. It is
preferable to only use this property when necessary, and to then
dispose the associated CancellationTokenSource instance at the
earliest opportunity (disposing the source will dispose of this
allocated handle). The handle should not be closed or disposed
directly.
Instead, I find the pattern with a Task.Delay more clean and readable:
public static async Task CheckTask(CancellationToken token)
{
do
{
// Do some processing
Console.WriteLine("Processing");
await Task.Delay(1500, token);
} while (!token.IsCancellationRequested);
Console.WriteLine("Bye bye");
}
And then when you want to stop your Task, cancel its via CancellationTokenSource.