Tasks cancellation and continue with - c#

I'm perplex on the cancellation of several tasks and continue with a task to display result.
According to what I understood, this program should display
Tasks canceled
CancellationTokenSource cts = new CancellationTokenSource();
List<Task> tasks = new List<Task>();
for(int i= 0; i<3; i++)
{
tasks.Add(Task.Run(() =>{
while(!cts.Token.IsCancellationRequested)
Thread.Sleep(500);
// Uncomment this to see 'Tasks canceled' in the result
//if(cts.Token.IsCancellationRequested)
// cts.Token.ThrowIfCancellationRequested();
},cts.Token));
}
Task.WhenAll(tasks).ContinueWith(task =>
{
if(task.IsCanceled)
Console.WriteLine("Tasks canceled");
if(task.IsCompleted)
Console.WriteLine("Tasks completed");
});
Thread.Sleep(2000);
cts.Cancel();
Unfortunately it display
Tasks completed
If i uncomment the throwing of cancel exception the program display
Tasks canceled
Tasks completed
Why? it seems that I missed something but I do not see what...

In first case this is what your code says it to do - stop looping when cancellation is requesting. After it exits loop, there is nothing more to execute hence task is complete.
By design to really cancel task, you need to throw exception to change execution flow. In fact, if statement is not needed in your code sample - that ThrowIfCancellationRequested method will handle this check by itself and will throw exception if cancellation has been requested.
Regarding last part, here is excerpt from MSDN:
IsCompleted will return true when the task is in one of the three final states: RanToCompletion, Faulted, or Canceled.
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.iscompleted(v=vs.110).aspx
So the task is complete even if it is was cancelled.

based on the doc
Task.WhenAll
Creates a task that will complete when all of the Task objects in an
array have completed.
so the task is guarantee to be in a complete state after all of your 3 tasks ended
What you want is probably
Task.WhenAll(tasks).Wait();
Foreach(Task task in tasks)
{
if(task.IsCanceled)
Console.WriteLine("Tasks canceled");
if(task.IsCompleted)
Console.WriteLine("Tasks completed");
}
Edit: The reason why the task is not canceled is because you never throw that exception. Lets imagine this case: Your task is almost done when you call cts.Cancel(); Right after you call it the task completed. So nothing in your task is canceled and thus its not marked as canceled.

Related

How to force an ActionBlock to complete fast

According to the documentation:
A dataflow block is considered completed when it is not currently processing a message and when it has guaranteed that it will not process any more messages.
This behavior is not ideal in my case. I want to be able to cancel the job at any time, but the processing of each individual action takes a long time. So when I cancel the token, the effect is not immediate. I must wait for the currently processed item to complete. I have no way to cancel the actions directly, because the API I use is not cancelable. Can I do anything to make the block ignore the currently running action, and complete instantly?
Here is an example that demonstrates my problem. The token is canceled after 500 msec, and the duration of each action is 1000 msec:
static async Task Main()
{
var cts = new CancellationTokenSource(500);
var block = new ActionBlock<int>(async x =>
{
await Task.Delay(1000);
}, new ExecutionDataflowBlockOptions() { CancellationToken = cts.Token });
block.Post(1); // I must wait for this one to complete
block.Post(2); // This one is ignored
block.Complete();
var stopwatch = Stopwatch.StartNew();
try
{
await block.Completion;
}
catch (OperationCanceledException)
{
Console.WriteLine($"Canceled after {stopwatch.ElapsedMilliseconds} msec");
}
}
Output:
Canceled after 1035 msec
The desired output would be a cancellation after ~500 msec.
Based on this excerpt from your comment...:
What I want to happen in case of a cancellation request is to ignore the currently running workitem. I don't care about it any more, so why I have to wait for it?
...and assuming you are truly OK with leaving the Task running, you can simply wrap the job you wish to call inside another Task which will constantly poll for cancellation or completion, and cancel that Task instead. Take a look at the following "proof-of-concept" code that wraps a "long-running" task inside another Task "tasked" with constantly polling the wrapped task for completion, and a CancellationToken for cancellation (completely "spur-of-the-moment" status, you will want to re-adapt it a bit of course):
public class LongRunningTaskSource
{
public Task LongRunning(int milliseconds)
{
return Task.Run(() =>
{
Console.WriteLine("Starting long running task");
Thread.Sleep(3000);
Console.WriteLine("Finished long running task");
});
}
public Task LongRunningTaskWrapper(int milliseconds, CancellationToken token)
{
Task task = LongRunning(milliseconds);
Task wrapperTask = Task.Run(() =>
{
while (true)
{
//Check for completion (you could, of course, do different things
//depending on whether it is faulted or completed).
if (!(task.Status == TaskStatus.Running))
break;
//Check for cancellation.
if (token.IsCancellationRequested)
{
Console.WriteLine("Aborting Task.");
token.ThrowIfCancellationRequested();
}
}
}, token);
return wrapperTask;
}
}
Using the following code:
static void Main()
{
LongRunningTaskSource longRunning = new LongRunningTaskSource();
CancellationTokenSource cts = new CancellationTokenSource(1500);
Task task = longRunning.LongRunningTaskWrapper(3000, cts.Token);
//Sleep long enough to let things roll on their own.
Thread.Sleep(5000);
Console.WriteLine("Ended Main");
}
...produces the following output:
Starting long running task
Aborting Task.
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Finished long running task
Ended Main
The wrapped Task obviously completes in its own good time. If you don't have a problem with that, which is often not the case, hopefully, this should fit your needs.
As a supplementary example, running the following code (letting the wrapped Task finish before time-out):
static void Main()
{
LongRunningTaskSource longRunning = new LongRunningTaskSource();
CancellationTokenSource cts = new CancellationTokenSource(3000);
Task task = longRunning.LongRunningTaskWrapper(1500, cts.Token);
//Sleep long enough to let things roll on their own.
Thread.Sleep(5000);
Console.WriteLine("Ended Main");
}
...produces the following output:
Starting long running task
Finished long running task
Ended Main
So the task started and finished before timeout and nothing had to be cancelled. Of course nothing is blocked while waiting. As you probably already know, of course, if you know what is being used behind the scenes in the long-running code, it would be good to clean up if necessary.
Hopefully, you can adapt this example to pass something like this to your ActionBlock.
Disclaimer & Notes
I am not familiar with the TPL Dataflow library, so this is just a workaround, of course. Also, if all you have is, for example, a synchronous method call that you do not have any influence on at all, then you will obviously need two tasks. One wrapper task to wrap the synchronous call and another one to wrap the wrapper task to include continuous status polling and cancellation checks.

Handle exception with CancellationToken.Register properly

I am trying to cancel a time consuming task after a certain milliseconds and for my case, I think CancellationToken.Register method best fits compared to other approaches with constant polling or WaitHandle. CancellationToken.Register method will help me define a delegate where I plan to take the task to cancelled state and stop further execution of the task; this delegate will be invoked when the task is cancelled (certain milliseconds later as per my goal). Here is the test code I have which I intend to extend for multiple tasks and with nested tasks later:
List<Task> tasks = new List<Task>();
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = tokenSource.Token;
Task t1 = Task.Factory.StartNew(() =>
{
// check cancellation token before task begins
if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested();
// register a callback to handle cancellation token anytime it occurs
cancellationToken.Register(() =>
{
Console.WriteLine("Task t1 cancelled");
cancellationToken.ThrowIfCancellationRequested();
});
// simulating a massive task; note, it is not a repeating task
Thread.Sleep(12000);
}, cancellationToken);
tasks.Add(t1);
try
{
// cancel token after 3000 ms + wait for all tasks
tokenSource.CancelAfter(3000);
Task.WaitAll(tasks.ToArray());
// OR wait for all tasks for 3000 ms and then cancel token immediately
//Task.WaitAll(tasks.ToArray(), 3000);
//tokenSource.Cancel();
}
catch (AggregateException e)
{
Console.WriteLine("\nAggregateException thrown with the following inner exceptions:");
// Display information about each exception.
foreach (var v in e.InnerExceptions)
{
if (v is TaskCanceledException)
Console.WriteLine(" TaskCanceledException: Task {0}", ((TaskCanceledException)v).Task.Id);
else
Console.WriteLine(" Exception: {0}", v.GetType().Name);
}
Console.WriteLine();
}
finally
{
tokenSource.Dispose();
}
I am, however, facing an issue with exception handling during execution of the callback method inside cancellationToken.Register. The call to cancellationToken.ThrowIfCancellationRequested() gives me exception "OperationCanceledException was unhandled by user code" followed by "AggregateException was unhandled". I have read about VS settings with unchecking User-unhandled exception for the first OperationCanceledException exception but my application terminates after second AggregateException exception; the try..catch block for Task.WaitAll does not seem to handle this.
I tried to enclose cancellationToken.ThrowIfCancellationRequested() in a try..catch block but the problem with this approach was that the task continued with the remaining steps which I do not desire. I do not see this behaviour with polling approach.
// poll continuously to check for cancellation instead of Register
// but I do not want my massive task inside this repeating block
while (true)
{
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Task t1 Canceled.");
cancellationToken.ThrowIfCancellationRequested();
}
}
What am I doing wrong with the CancellationToken.Register approach?
The error you see is exactly because you didn't wrap the ThrowIfCancellationRequested inside a try-catch block.
In my opinion it really depends on what you're doing in place of that Sleep().
Ending the task in a cooperative way like
while(!cancellationToken.IsCancellationRequested)
{
// Do stuff
// Also check before doing something that may block or take a while
if(!cancellationToken.IsCancellationRequested)
{
Stream.Read(buffer, 0, n);
}
}
should be the best way to stop it.
If you really need to stop it no matter what, I would do wrap it in another task
Task.Run(() =>
{
// Simulate a long running task
Thread.Sleep(12*1000);
}, cancellationToken);
(tested and working)
This way you should not see any Exception Unhandled thrown.
Also, you might want to take a look at this: How do I abort/cancel TPL Tasks?

ContinueWith TaskContinuationOptions.OnlyOnFaulted does not seem to catch an exception thrown from a started task

I'm trying to catch an exception thrown from a task method using ContinueWith and OnlyOnFaulted like below. However I get an unhandled exception while I try to run this code.
I'd like the task to run to completion since I have handled the exception already. But Task.Wait() runs into AggregateException.
var taskAction = new Action(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();
If I handle the exception in the task method like below, everything will go normal as I expect. Task Runs Into Completion, Exception gets logged and Wait succeeds also.
var taskAction = new Action(() =>
{
try
{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
}
catch (Exception ex)
{
Console.WriteLine("Catching the exception in the Action catch block only");
}
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x=> Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();
My question is: Am I using the OnlyOnFaulted correctly or is it always better to handle the exception in the task method itself? I would like the main thread to continue even if task runs into exception. Also, I want to log that exception from task method.
Note: I have to wait for the task method to complete before going further (with or without errors).
To summarize(my understanding so far)
If exception from Task is handled i.e. if wait or await catches the exception then the exception would be propagated to continuedtask onfaulted.
Exception can be caught even in the task method and consumed\handled.
try
{
t.wait();
}
catch(Exception e)
{
LogError(e);
}
In above case before LogError gets called, continued task associated with the main task's onfaulted gets executed.
First of all, you aren't using OnlyOnFaulted correctly. When you use ContinueWith on a task you don't really change that task, you get back a task continuation (which in your case you disregard).
If the original task faulted (i.e. had an exception thrown inside it) it would stay faulted (so calling Wait() on it would always rethrow the exception). The continuation however would run after the task faulted and handle the exception.
That means that in your code you do handle the exception, but you're also rethrowing it with Wait(). The correct code should look like this:
Task originalTask = Task.Run(() => throw new Exception());
Task continuationTask = originalTask.ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
continuationTask.Wait()
// Both tasks completed. No exception rethrown
Now, as Yuval Itzchakov pointed out, you can handle the exception wherever you want but it would better if you were utilizing async-await to wait asynchronously if you can (you can't in Main) instead of blocking with Wait():
try
{
await originalTask;
}
catch (Exception e)
{
// handle exception
}
Am I using the TaskContinutationOptions.OnlyOnFaulted correctly or is it always better to
handle the exception in the task method itself? I would like the main
thread to continue even if task runs into exception.
You can handle exceptions either way, inside or outside. It's a matter of preference and usually depends on the use-case.
Note that one thing you aren't doing is keeping a reference to your continuation. You're using Task.Wait on the original task which propogates the exception regardless of the fact that you have a continuation which handles it.
One thing that bothers me is that you're using Task.Wait which synchronously waits instead of await which asynchronously waits. Thats the reason for the AggregationException. More so, you shouldn't block on asynchronous operations, as that will lead you down a rabit hole you probably don't want to go, with all sorts of synchronization context problems.
What i would personally do, is use await inside of ContinueWith, because its the less verbose option. Also, i'd use Task.Run over Task.Factory.StartNew:
var task = Task.Run(() =>
{
Thread.Sleep(1000);
throw new InvalidOperationException();
}
// Do more stuff here until you want to await the task.
try
{
await task;
}
catch (InvalidOperationException ioe)
{
// Log.
}
The original question happens to run its taskAction on a separate thread. That might not always be the case.
The answer by i3arnon solves the problem well. What if we do not want to use a separate thread? If we want to simply start a task, running it synchronously until we hit IO or a delay, and then continue about our own business. Only at the very end, we will wait for the task to complete.
How do we await the task with rethrowing its exception? Instead of wrapping it in Task.Run(), we will use an empty continuation that will always succeed when the task finishes for any reason.
// Local function that waits a moment before throwing
async Task ThrowInAMoment()
{
await Task.Delay(1000);
Console.WriteLine("Task waited for a sec");
throw new Exception("Throwing for example");
}
// Start the task without waiting for it to complete
var t = ThrowInAMoment();
// We reach this line as soon as ThrowInAMoment() can no longer proceed synchronously
// This is as soon as it hits its "await Delay(1000)"
// Handle exceptions in the task
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
// Continue about our own business
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
// Now we want to wait for the original task to finish
// But we do not care about its exceptions, as they are already being handled
// We can use ContinueWith() to get a task that will be in the completed state regardless of HOW the original task finished (RanToCompletion, Faulted, Canceled)
await t.ContinueWith(task => {});
// Could use .Wait() instead of await if you want to wait synchronously for some reason
It looks like you are on the right track. I have ran your code and get the same results. My suggestion is to use the try/catch directly from inside the action. This will make your code clearer and allow you to log things like which thread it came from, which may be different in the continuation path.
var taskAction = new Action(() =>
{
try{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
}
catch (Exception e)
{
Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ e);
}
});
Task t = Task.Factory.StartNew(taskAction);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
await t;
[Edit] Removed outer exception handler.

CancellationTokenSource not behaving as expected

what's expected in this case, is that if the user cancels the task by hitting enter, the other task hooked by ContinueWith will run, but it's not the case, as per an AggregateException keeps thrown despite the explicit handling in the ContinueWith which is apparently not being executed.
any clarification on the below please?
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokensource = new CancellationTokenSource();
CancellationToken token = tokensource.Token;
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
t.Exception.Handle((e) => true);
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
Console.WriteLine("Press any key to cancel");
Console.ReadLine();
tokensource.Cancel();
task.Wait();
}
}
Let's start with a few facts:
When you pass a CancellationToken as a parameter for Task.Run it only has an effect if it's cancelled before the task started running. If the task is already running it will not be canceled.
To get a task canceled after it has started running, you need to use CancellationToken.ThrowIfCancellationRequested, not CancellationToken.IsCancellationRequested.
If a task is canceled, its Exception property doesn't hold any exceptions and is null.
If a continuation task does not run for some reason, that means it was canceled.
A task contains exceptions from itself + all its child tasks (hence, AggregateException).
So this is what happens in your code:
The task starts running, because the token is not canceled. It will run until the token gets canceled. After it will end the continuation will not run because it only runs when the preceding task is canceled, and it hasn't been. When you Wait the task it will throw an AggregateException with a TaskCanceledException because the continuation was canceled (if you would remove that continuation the exception will go away).
Solution:
You need to fix the task so it would actually be canceled, and remove (or null check) the exception handling because there is no exception:
var task = Task.Run(new Action(() =>
{
while (true)
{
token.ThrowIfCancellationRequested();
Console.Write("*");
Thread.Sleep(1000);
}
}), token).ContinueWith(
t => Console.WriteLine("You have canceled the task"),
TaskContinuationOptions.OnlyOnCanceled);
If you pass the token as the second parameter, the task won't continue on nicely because it's really been cancelled. Instead it throws an OperationCanceledException which gets wrapped in an AggregateException. This is entirely expected. Now, if you did NOT pass the token to the task constructor then you'd see the behaviour you'd expect because because you'd only be using the token as a flag for exiting the while loop. In that case you're not really cancelling the task, you're exiting the while loop and completing the task normally.

Cancellation of a task

I tried to run a simple example on the cancellation of a task like the one below
CancellationTokenSource tokenSource2 = new CancellationTokenSource();
CancellationToken token2 = tokenSource2.Token;
Task task2 = new Task(() =>
{
for (int i = 0; i < int.MaxValue; i++)
{
token2.ThrowIfCancellationRequested();
Thread.Sleep(100);
Console.WriteLine("Task 2 - Int value {0}", i);
}
}, token2);
task2.Start();
Console.WriteLine("Press any key to cancel the task");
Console.ReadLine();
tokenSource2.Cancel();
Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);
I expected that Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled); would print **"Task 2 cancelled? True"**, but it printed "False".
Do you know what happened? Is that the expected behaviour? Thanks.
EDIT: to ensure that the task hasn't completed before the cancellation request is called. I added the Console.ReadLine().
First, maybe you misunderstand what IsCanceled means? It doesn't mean “this Task is pending cancellation, so it should complete shortly”, it means “this Task has been canceled, it is now complete”.
If you didn't misunderstand that, think about what exactly the sequence of events is. What happens is this:
ThrowIfCancellationRequested() is called, but the token wasn't canceled yet, so it doesn't throw.
Thread.Sleep() is called, so the thread running the Task sleeps.
Cancel() is called.
IsCanceled is checked. The code in the Task didn't have a chance to realize that the token was canceled, so it's still running, so IsCanceled returns false.
ThrowIfCancellationRequested() is called again, this time it throws, which actually cancels the Task.
This is why ISCanceled is returning false to you. If you want it to return true, you could add something like Thread.Sleep(150) before checking IsCanceled, or, even better, actually wait for the Task to complete.
The Task ended before you call for cancellation, take a look at the following below this may help to shed some more light on how to resolve your issue
By reading from here http://social.msdn.microsoft.com/Forums/da-DK/parallelextensions/thread/9f88132a-f8bd-4885-ab63-645d7b6c2127 it seems that the token is used to cancel the task BEFORE the task is "really" started, but after it has been queued.
It's more a way to cancel a task that's scheduled to occur, but not started yet. Once the task is running, the only way to cancel it is cooperatively via your own checking within the method. Without this, you'd have to always start the task, then check it internally, which would add a lot of extra, unnecessary overhead
You can even read it from Cancellation token in Task constructor: why?
This is not an answer but too long to write in a comment
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;
Task task = new Task(() =>
{
for (int i = 0; i < int.MaxValue; i++)
{
cancellationToken.ThrowIfCancellationRequested();
Console.WriteLine("Task 2 - Int value {0}", i);
}
}, cancellationToken);
task.Start();
cancellationTokenSource.Cancel();
try
{
task.Wait();
}
catch (AggregateException ae)
{
if(ae.InnerExceptions.Single() is TaskCanceledException)
Console.WriteLine("Caught TaskCanceledException");
else
Console.WriteLine("Did not catch canceled");
}
Console.WriteLine("Task 2 cancelled? {0}", task.IsCanceled);
The code above prints what is expected, and I never got a print of 'Task 2 - Int value {0}' so I don't think it finished before canceling.
Note that canceling the task throws an exception in my sample
There are some patterns for handling exceptions using tasks that can be good to read about.

Categories