Cancelling a Task is throwing an exception
You are explicitly throwing an Exception on this line:
cancelToken.ThrowIfCancellationRequested();
If you want to gracefully exit the task, then you simply need to get rid of that line.
Typically people use this as a control mechanism to ensure the current processing gets aborted without potentially running any extra code. Also, there is no need to check for cancellation when calling ThrowIfCancellationRequested()
since it is functionally equivalent to:
if (token.IsCancellationRequested)
throw new OperationCanceledException(token);
When using ThrowIfCancellationRequested()
your Task might look more like this:
int CalculatePrime(CancellationToken cancelToken, object digits) {
try{
while(true){
cancelToken.ThrowIfCancellationRequested();
//Long operation here...
}
}
finally{
//Do some cleanup
}
}
Also, Task.Wait(CancellationToken)
will throw an exception if the token was cancelled. To use this method, you will need to wrap your Wait call in a Try...Catch
block.
MSDN: How to Cancel a Task
Task Cancellation Throwing Exception
This line
token.ThrowIfCancellationRequested();
explicitly throws an exception. What the link was telling you is that if the token of the task matches the token in the OperationCanceledException
that has just been thrown, "the Task transitions to the Canceled state (rather than the Faulted state)."
So the bottom line is if you don't want an exception to be thrown when the task is canceled, simply omit that line!
Cancel all async methods if one throws an exception
Because the relevant part of your code is:
try
{
...
await secondTask;
await firstTask;
}
catch(...)
{
source.Cancel();
}
Now while the firstTask is started and has thrown, it is awaited after the secondTask. The exception won't surface in the caller until the task is awaited. And so the catch clause will only execute after secondTask has already completed. The Cancel() is just happening too late.
If you want your firstTask to interrupt the second one you will have to
- pass the source into FirstAsync and call Cancel() there. A little ugly.
- change the await structure. I think your sample is a little artificial. Use Parallel.Invoke() or something similar and it will happen quite naturally.
cancelling tasks throws exception but task continues
Use Task.Run
instead of Task.Factory.StartNew
and try to avoid mixing Task
and Thread.Sleep
. Use Task.Delay
. If using Task
then the code needs to be async all the way.
Your loop continues because there is nothing to break out of the loop.
A rewrite of the above example with proper syntax would look like this
public class Program {
public static async Task Main(string[] args) {
Console.WriteLine("Hello");
await CancelingTasks2();
Console.WriteLine("Exit");
}
private static async Task CancelingTasks2() {
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
var t = print("printing for ever ...", token);
await Task.Delay(2000);
cts.Cancel();
Console.WriteLine("canceled");
Console.WriteLine("task status " + t.Status);
Console.WriteLine("token IsCancellationRequested " + token.IsCancellationRequested);
}
private static async Task print(string txt, CancellationToken token) {
while (true) {
if (token.IsCancellationRequested)
throw new OperationCanceledException("cancelled on the token", token);
Console.WriteLine(txt);
await Task.Delay(500);
}
}
}
And produce the following output when run
Hello
printing for ever ...
printing for ever ...
printing for ever ...
printing for ever ...
printing for ever ...
canceled
task status WaitingForActivation
token IsCancellationRequested True
Exit
Fiddle
Waiting for multiple tasks when some might be cancelled
I think the problem is the TaskContinuationOption
you pass. In your example you use OnlyOnCanceled
. So it only continues with that if a task was cancelled.
I'm not sure what the desired behaviour is when a task was cancelled. If you only want to proceed when none of them was cancelled, you could use NotOnCanceled
. If you want to proceed in either case, with cancelled tasks or not, then you could for example use NotOnFaulted
, since cancellation is not regarded as fault.
Canceling task if a parallel task throw an exception
Solved with @noobed comment.
try
{
//fetch Entity1 results into entity1List
}
catch
{
cts.Cancel();
}
In case of general exception it will be catch, and the cts can be called with cancel.
Async Tasks, Cancellation, and Exceptions
I always recommend people read the Cancellation in Managed Threads docs. It's not quite complete; like most MSDN docs, it tells you what you can do, not what you should do. But it's definitely more clear than the dotnet docs on cancellation.
The example shows the basic usage
First, it's important to note that the cancellation in your example code only cancels the task - it does not cancel the underlying operation. I strongly recommend that you do not do this.
If you want to cancel the operation, then you would need to update RunFoo
to take a CancellationToken
(see below for how it should use it):
public Task Run(IFoo foo, CancellationToken token = default(CancellationToken)) {
var tcs = new TaskCompletionSource<object>();
// Regular finish handler
EventHandler<AsyncCompletedEventArgs> callback = (sender, args) =>
{
if (args.Cancelled)
{
tcs.TrySetCanceled(token);
CleanupFoo(foo);
}
else
tcs.TrySetResult(null);
};
RunFoo(foo, token, callback);
return tcs.Task;
}
If you can't cancel foo
, then don't have your API support cancellation at all:
public Task Run(IFoo foo) {
var tcs = new TaskCompletionSource<object>();
// Regular finish handler
EventHandler<EventArgs> callback = (sender, args) => tcs.TrySetResult(null);
RunFoo(foo, callback);
return tcs.Task;
}
Callers can then perform a cancelable wait on the task, which is a much more appropriate code technique for this scenario (since it is the wait that is canceled, not the operation represented by the task). Performing a "cancelable wait" can be done via my AsyncEx.Tasks library, or you could write your own equivalent extension method.
The documentation says either just returning and having the task status switch to RanToCompletion, or throwing an OperationCanceledException (which results in the task's result being Canceled) is fine.
Yeah, those docs are misleading. First, please don't just return; your method would complete the task successfully - indicating the operation completed successfully - when in fact the operation did not complete successfully. This may work for some code, but is certainly not a good idea in general.
Normally, the proper way to respond to a CancellationToken
is to either:
- Periodically call
ThrowIfCancellationRequested
. This option is better for CPU-bound code. - Register a cancellation callback via
Register
. This option is better for I/O-bound code. Note that registrations must be disposed!
In your particular case, you have an unusual situation. In your case, I would take a third approach:
- In your "per-frame work", check
token.IsCancellationRequested
; if it is requested, then raise the callback event withAsyncCompletedEventArgs.Cancelled
set totrue
.
This is logically equivalent to the first proper way (periodically calling ThrowIfCancellationRequested
), catching the exception, and translating it into an event notification. Just without the exception.
I always get a TaskCanceledException if I await the task returned. My guess would be that this is normal behaviour (I hope it is) and that I'm expected to wrap a try/catch around the call when I want to use cancellation.
The proper consuming code for a task that can be canceled is to wrap the await
in a try/catch and catch OperationCanceledException
. For various reasons (many historical), some APIs will cause OperationCanceledException
and some will cause TaskCanceledException
. Since TaskCanceledException
derives from OperationCanceledException
, consuming code can just catch the more general exception.
But I guess if a user wants to distinguish between a task that finished normally and one that was canceled, the [cancellation exception] is pretty much a side-effect of ensuring that, right?
That's the accepted pattern, yes.
Documentation suggests that any exceptions, even those that pertain to cancellation, are wrapped in an AggregateException by the TPL.
This is only true if your code synchronously blocks on a task. Which it should really avoid doing in the first place. So the docs are definitely misleading again.
However, in my tests I'd always get the TaskCanceledException directly, without any wrapper.
await
avoids the AggregateException
wrapper.
Update for comment explaining CleanupFoo
is a cancellation method.
I'd first recommend trying to use CancellationToken
directly within the code initiated by RunFoo
; that approach would almost certainly be easier.
However, if you must use CleanupFoo
for cancellation, then you'll need to Register
it. You'll need to dispose that registration, and the easiest way to do this may actually be to split it into two different methods:
private Task DoRun(IFoo foo) {
var tcs = new TaskCompletionSource<object>();
// Regular finish handler
EventHandler<EventArgs> callback = (sender, args) => tcs.TrySetResult(null);
RunFoo(foo, callback);
return tcs.Task;
}
public async Task Run(IFoo foo, CancellationToken token = default(CancellationToken)) {
var tcs = new TaskCompletionSource<object>();
using (token.Register(() =>
{
tcs.TrySetCanceled(token);
CleanupFoo();
});
{
var task = DoRun(foo);
try
{
await task;
tcs.TrySetResult(null);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
await tcs.Task;
}
Properly coordinating and propagating the results - while preventing resource leaks - is quite awkward. If your code could use CancellationToken
directly, it would be much cleaner.
How to properly cancel Task.WhenAll and throw the first exception?
You shouldn't use ContinueWith
. The correct answer is to introduce another "higher-level" async
method instead of attaching a continuation to each task:
private async Task DoSomethingWithCancel(CancellationTokenSource cts)
{
try
{
await DoSomethingAsync(cts.Token).ConfigureAwait(false);
}
catch
{
cts.Cancel();
throw;
}
}
var cts = new CancellationTokenSource();
try
{
var tasks = new Task[] { DoSomethingWithCancel(cts), ... };
await Task.WhenAll(tasks).ConfigureAwait(false);
}
catch (SpecificException)
{
...
}
Related Topics
JSONserializersettings and ASP.NET Core
What Is the JSON.Net Equivalent of Xml's Xpath, Selectnodes, Selectsinglenode
How to Downgrade from Visual Studio 2012 Project to Visual Studio 2008
Why the Default Synchronizationcontext Is Not Captured in a Console App
Call a Method from Another Form
Awaiting Asynchronous Function Inside Formclosing Event
Request Windows Vista Uac Elevation If Path Is Protected
File Getting Copied to Syswow64 Instead of System32
How to Get the Actual Monitor Name? as Seen in the Resolution Dialog
Yield Statement Implementation
Serialize a Container of Enums as Strings Using JSON.Net
Is It Ok to Use a String as a Lock Object
Make ASP.NET Wcf Convert Dictionary to JSON, Omitting "Key" & "Value" Tags
Force Browser to Download PDF Document Instead of Opening It
Does Stream.Dispose Always Call Stream.Close (And Stream.Flush)