Reputation: 83
I don't really understand this. In my code below, run as-is, the exception thrown by token.ThrowIfCancellationRequested() is not being sent back to where Main is using .Wait(), and is instead thrown on the spot with the stacktrace not really knowing where it happened other than "A task was cancelled". However if I remove the async keyword and remove the try catch block with the await Task.Delay(), it does get sent back to .Wait() in main and is caught there.
Am I doing something wrong, or how exactly do I get both the exception thrown by await Task.Delay() and token.ThrowIfCancellationRequested() to both bubble up to .Wait()?
static void Main(string[] args)
{
var t = new CancellationTokenSource();
var task = Task.Factory.StartNew((x) => Monitor(t.Token), t.Token, TaskCreationOptions.LongRunning);
while (true)
{
if (Console.ReadLine() == "cancel")
{
t.Cancel();
try
{
task.Wait();
}
catch(AggregateException)
{
Console.WriteLine("Exception via task.Wait()");
}
Console.WriteLine("Cancelled");
}
}
}
static async void Monitor(CancellationToken token)
{
while(true)
{
for(int i = 0; i < 5; i++)
{
// If the async in the method signature, this does not send the exception to .Wait();
token.ThrowIfCancellationRequested();
// Do Work
Thread.Sleep(2000);
}
// Wait 10 seconds before doing work again.
// When this try block is removed, and the async is taken out of the method signature,
// token.ThrowIfCancellationRequested() properly sends the exception to .Wait()
try
{
await Task.Delay(10000, token);
}
catch(TaskCanceledException)
{
Console.WriteLine("Exception from Delay()");
return;
}
}
}
Upvotes: 2
Views: 2514
Reputation: 457057
You should avoid async void
. Its exception handling semantics are tricky, and it's not possible to compose into other async
methods.
async void
methods are conceptually event handlers, so if it throws an exception, it will be raised directly on its SynchronizationContext
- in this case, thrown on a thread pool thread.
The asynchronous equivalent of a void
-returning method is not an async
void
-returning method; it is an async
Task
-returning method. So your Monitor
method should return Task
:
static void Main(string[] args)
{
var t = new CancellationTokenSource();
var task = Monitor(t.Token);
while (true)
{
if (Console.ReadLine() == "cancel")
{
t.Cancel();
try
{
task.Wait();
}
catch(AggregateException)
{
Console.WriteLine("Exception via task.Wait()");
}
Console.WriteLine("Cancelled");
}
}
}
static async Task Monitor(CancellationToken token)
Don't worry about missing the LongRunning
flag; it's just an optimization and the thread pool will work fine without it.
You may find my async
/await
intro or the official MSDN documentation helpful.
Upvotes: 2