我从三个不同的任务中抛出一个 OperationCanceledException
,每个任务都有细微的差别,如下代码所示:
static async Task ThrowCancellationException()
{
throw new OperationCanceledException();
}
static void Main(string[] args)
{
var t1 = new Task(() => throw new OperationCanceledException());
t1.Start();
try { t1.Wait(); } catch { }
Task t2 = new Task(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Wait(); } catch { }
Task t3 = ThrowCancellationException();
Console.WriteLine(t1.Status); // prints Faulted
Console.WriteLine(t2.Status); // prints RanToCompletion
Console.WriteLine(t3.Status); // prints Canceled
}
我的问题是:
为什么每个任务的状态不同?
我可以理解,标记有 async
的代码/lambda 和未标记 async
的 lambda 之间存在差异,但即使在 之间,状态也不同async
lambda 和运行相同代码的 async
方法。
最佳答案
I could understand that there are differences between the code/lambdas marked with async and the lambda not marked with async but the status is different even between the async lambda and the async method running the same code.
这并不完全正确。
如果你仔细观察 new Task(async () => throw new OperationCanceledException())
,你会发现它正在调用重载 new Task(Action action)
(没有重载需要 Func<Task>
)。这意味着它相当于传递 async void
方法,而不是 async Task
方法。
所以:
Task t2 = new Task(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Wait(); } catch { }
编译后的结果如下:
private static async void CompilerGeneratedMethod()
{
throw new OperationCanceledException()
}
...
Task t2 = new Task(CompilerGeneratedMethod);
t2.Start();
try { t2.Wait(); } catch { }
这会从线程池中获取一个线程,并在其上运行 CompilerGeneratedMethod
。当从 async void
方法内部抛出异常时,该异常会在适当的地方重新抛出(在本例中,它是在 ThreadPool 上重新抛出),但 CompilerGeneratedMethod
方法本身会立即返回。这会导致 Task t2
立即完成,这就是它的状态为 RanToCompletion
的原因。
那么异常是怎么回事呢?它即将导致您的申请失败!在 Console.ReadLine
的末尾粘贴 Main
,并在您有机会按 Enter 之前看到应用程序退出。
这个:
Task t3 = ThrowCancellationException();
非常不同。它不会尝试在线程池上运行任何内容。 ThrowCancellationException
同步运行,并同步返回包含 Task
的 OperationCanceledException
。包含 Task
的 OperationCanceledException
被视为 Canceled
。
如果您想在 ThreadPool 上运行 async
方法,请使用 Task.Run
。它有一个重载,需要 Func<Task>
,这意味着:
Task t2 = Task.Run(async () => throw new OperationCanceledException());
编译为如下内容:
private static async Task CompilerGeneratedMethod()
{
throw new OperationCanceledException();
}
...
Task t2 = Task.Run(CompilerGeneratedMethod);
这里,当 CompilerGeneratedMethod
在 ThreadPool 上执行时,它返回包含 Task
的 OperationCanceledException
。然后,任务机制将 Task t2
转换为 Canceled
状态。
顺便说一句,请避免 new Task
,如果您想在 ThreadPool 上显式运行方法,则更喜欢使用 Task.Run
。 TPL中有很多方法是在async/await之前引入的,与它一起使用时会很困惑。
关于c# - 任务中的类似代码返回不同的状态代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60113345/