问题描述
我不太了解这一点.在我下面的代码中,按原样运行,令牌引发的异常.ThrowIfCancellationRequested()不会被发送回Main使用.Wait()的位置,而是与stacktrace一起当场抛出,并不真正知道它发生的位置除了任务已取消".但是,如果我删除async关键字并使用await Task.Delay()删除try catch块,它的确会发送回main中的.Wait()并在那里被捕获.
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.
我是在做错什么,还是我究竟是怎么得到等待Task.Delay()和token.ThrowIfCancellationRequested()引发的异常,两者都冒泡到.Wait()?
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;
}
}
}
推荐答案
您应避免使用async void
.它的异常处理语义很棘手,并且不可能组合到其他async
方法中.
You should avoid async void
. Its exception handling semantics are tricky, and it's not possible to compose into other async
methods.
async void
方法在概念上是事件处理程序,因此,如果引发异常,它将直接在其SynchronizationContext
上引发-在这种情况下,将引发在线程池线程上.
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.
void
-返回方法的异步等效项不是async
void
-返回方法.这是async
Task
返回方法.因此,您的Monitor
方法应返回Task
:
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)
不必担心会丢失LongRunning
标志;这只是一个优化,没有它,线程池也可以正常工作.
Don't worry about missing the LongRunning
flag; it's just an optimization and the thread pool will work fine without it.
您可能会找到我的 async
/await
简介或官方MSDN文档有用.
You may find my async
/await
intro or the official MSDN documentation helpful.
这篇关于在方法签名中使用异步时,任务取消异常不会冒泡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!