取消任务时的OperationCanceledException与VS TaskCanceledException

编程入门 行业动态 更新时间:2024-10-23 23:24:24
本文介绍了取消任务时的OperationCanceledException与VS TaskCanceledException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

以下代码创建一个正在被取消的任务。 await 表达式(第一种情况)抛出 System.OperationCanceledException 而同步 Wait()(情况2)抛出 System.Threading.Tasks.TaskCanceledException (包装在 System.AggregateException 中)。

The following code creates a task which is being canceled. await expression (case 1) throws System.OperationCanceledException while synchronous Wait() (case 2) throws System.Threading.Tasks.TaskCanceledException (wrapped in System.AggregateException).

using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { Program.MainAsync().Wait(); } private static async Task MainAsync() { using(var cancellationTokenSource = new CancellationTokenSource()) { var token = cancellationTokenSource.Token; const int cancelationCheckTimeout = 100; var task = Task.Run( async () => { for (var i = 0; i < 100; i++) { token.ThrowIfCancellationRequested(); Console.Write("."); await Task.Delay(cancelationCheckTimeout); } }, cancellationTokenSource.Token ); var cancelationDelay = 10 * cancelationCheckTimeout; cancellationTokenSource.CancelAfter(cancelationDelay); try { await task; // (1) //task.Wait(); // (2) } 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())}"); } } } }

1输出:

..........System.OperationCanceledException: The operation was canceled. at System.Threading.CancellationToken.ThrowIfCancellationRequested() at Program.<>c__DisplayClass1_0.<<MainAsync>b__0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Program.<MainAsync>d__1.MoveNext() Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null

情况2输出:

..........System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled. --- End of inner exception stack trace --- at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Program.<MainAsync>d__1.MoveNext() ---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<--- Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null

为什么 System.AggregateException 在第二种情况下不包含 System.OperationCanceledException 作为内部异常?

Why System.AggregateException in the 2nd case doesn't contain System.OperationCanceledException as an inner exception?

我知道 ThrowIfCancellationRequested()抛出 OperationCanceledException ,我们可以看到在两种情况下任务处于取消状态(不是错误状态)。

I know that ThrowIfCancellationRequested() throws OperationCanceledException and we can see that in both cases Task gets to canceled (not faulty) state.

这让我感到困惑,因为从.NET API中取消方法在两种情况下都会产生一致的行为-已取消的任务仅使用系统抛出 TaskCanceledException :

This puzzles me because canceling a method from .NET API produces consistent behaviour in both cases - canceled task throws only TaskCanceledException:

using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { Program.MainAsync().Wait(); } private static async Task MainAsync() { using(var cancellationTokenSource = new CancellationTokenSource()) { var token = cancellationTokenSource.Token; var task = Task.Delay(1000, token); cancellationTokenSource.CancelAfter(100); try { await task; // (1) //task.Wait(); // (2) } 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())}"); } } } }

1输出:

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 Program.<MainAsync>d__1.MoveNext() Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null

情况2输出:

System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled. --- End of inner exception stack trace --- at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Program.<MainAsync>d__1.MoveNext() ---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<--- Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null

推荐答案

此处的区别来自使用 token.ThrowIfCancellationRequested()。此方法检查是否取消,如果请求,则专门抛出 OperationCanceledException 而不是 TaskCanceledException (可理解为 CancellationToken 并非TPL专有)。您可以查看参考源并看到它在调用此方法:

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); }

常规取消,尽管确实会产生 TaskCanceledException 。您可以看到,通过取消令牌 before ,任务有机会开始运行:

"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())}"); }

输出:

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

传统.Net方法通常不使用 CancellationToken.ThrowIfCancellationRequested 对于异步API,因为这仅在将工作卸载到另一个线程时才适用。这些方法用于固有的异步操作,因此可以使用 CancellationToken.Register (或内部 InternalRegisterWithoutEC )监视取消操作。

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).

更多推荐

取消任务时的OperationCanceledException与VS TaskCanceledException

本文发布于:2023-11-08 07:02:07,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1568670.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:OperationCanceledException   TaskCanceledException

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!