System.Threading.Tasks.TaskCanceledException
Represents an exception used to communicate task cancellation.
Minimum version: >= 4.0 >= Core 1.0
Statistics
How to handle it
try
{
}
catch (System.Threading.Tasks.TaskCanceledException e)
{
}
try
{
}
catch (System.Threading.Tasks.TaskCanceledException e) when (e.Message.Contains("something"))
{
}
try
{
}
catch (System.Threading.Tasks.TaskCanceledException e) when (LogException(e))
{
}
private static bool LogException(Exception e)
{
logger.LogError(...);
return false;
}
How to avoid it
We haven't written anything about avoiding this exception yet. Got a good tip on how to avoid throwing System.Threading.Tasks.TaskCanceledException? Feel free to reach out through the support widget in the lower right corner with your suggestions.
Links
YouTube videos
Possible fixes from StackOverflow
If you add ContinueWith()
with an empty action, the exception isn't thrown. The exception is caught and passed to the tsk.Exception
property in the ContinueWith()
. But It saves you from writing a try/catch that uglifies your code.
await Task.Delay(500, cancellationToken.Token).ContinueWith(tsk => { });
UPDATE:
Instead of writing code to handle the exception, a boolean would be much cleaner. This is only preferred when a delay cancel is expected!. One way is to create a helper class (Although I don't like helper classes much)
namespace System.Threading.Tasks
{
public static class TaskDelay
{
public static Task<bool> Wait(TimeSpan timeout, CancellationToken token) =>
Task.Delay(timeout, token).ContinueWith(tsk => tsk.Exception == default);
public static Task<bool> Wait(int timeoutMs, CancellationToken token) =>
Task.Delay(timeoutMs, token).ContinueWith(tsk => tsk.Exception == default);
}
}
For example:
var source = new CancellationTokenSource();
if(!await TaskDelay.Wait(2000, source.Token))
{
// The Delay task was canceled.
}
(don't forget to dispose the source)
The difference here comes from using token.ThrowIfCancellationRequested()
. This method checks for cancellation and if requested throws OperationCanceledException
specifically and not TaskCanceledException
(understandable as CancellationToken
isn't exclusive to the TPL). You can look at the reference source and see that it calls this method:
private void ThrowOperationCanceledException()
{
throw new OperationCanceledException(Environment.GetResourceString("OperationCanceled"), this);
}
"Regular" cancellation though will indeed generate a TaskCanceledException
. You can see that by cancelling the token before the task had a chance to start running:
cancellationTokenSource.Cancel();
var task = Task.Run(() => { }, cancellationTokenSource.Token);
try
{
await task;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}");
Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}");
Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}");
}
Output:
System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Sandbox.Program.<MainAsync>d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null
Traditional .Net methods usually don't use CancellationToken.ThrowIfCancellationRequested
for async API as this is only appropriate when offloading work to another thread. These methods are for inherently asynchronous operations so cancellation is monitored using CancellationToken.Register
(or the internal InternalRegisterWithoutEC
).
Your problem is that SmtpClient
is disposed before the email is sent.
Two ways:
Await the
SendMailAsync
resultusing (var client = new SmtpClient()) { await client.SendMailAsync(email); }
Register the
SendCompleted
event and dispose theSmtpClient
only after the message is sentvar client = new SmtpClient(); client.SendCompleted += (s, e) => { client.Dispose(); }; return client.SendMailAsync(message);
That's to be expected. When you cancel the old Delay
, it will raise an exception; that's how cancellation works. You can put a simple try
/catch
around the Delay
to catch the expected exception.
Note that if you want to do time-based logic like this, Rx is a more natural fit than async
.
TaskCanceledException
inherits from OperationCanceledException
. So it least there is a little consitency.
if( ex is OperationCanceledException)
{
...
}
Source: Stack Overflow