当前位置: 首页 > 编程语言 > C#

FailurewithCancelledTaskStatusafterCancellationToken.ThrowIfCancellationRequestedShare

时间:2023-04-10 16:54:34 C#

CancellationToken.ThrowIfCancellationRequestedAfterFailurewithCancelledTaskStatus通常我不回答问题,但这次我想引起一些注意,我认为这可能是一个模糊的呢常见问题。它是由这个问题触发的,从那以后我查看了我自己的旧代码,发现其中一些也受到了影响。下面的代码启动并等待两个几乎相同的任务,task1和task2。task1与task2的不同之处在于它运行一个永无止境的循环。这两种情况对于执行CPU密集型工作的某些真实场景来说都是非常典型的。使用系统;使用系统线程;使用System.Threading.Tasks;namespaceConsoleApplication{publicclassProgram{staticasyncTaskTestAsync(){varct=newCancellationTokenSource(millisecondsDelay:1000);vartoken=ct.Token;//启动任务1vartask1=Task.Run(()=>{for(vari=0;;i++){Thread.Sleep(i);//模拟工作项#itoken.ThrowIfCancellationRequested();}});//启动任务2vartask2=Task.Run(()=>{for(vari=0;i<1000;i++){Thread.Sleep(i);//模拟工作项#itoken.ThrowIfCancellationRequested();}});//awaittask1try{awaittask1;}catch(Exceptionex){Console.WriteLine(new{task="task1",ex.Message,task1.Status});}//awaittask2try{awaittask2;}catch(Exceptionex){Console.WriteLine(new{task="task2",ex.Message,task2.Status});}}publicstaticvoidMain(string[]args){TestAsync().Wait();Console.WriteLine("输入退出...");控制台.ReadLine();}}}FiddlehereOutput:{task=task1,Message=Operationcanceled.,status=canceled}{task=task2,Message=操作已取消。,Status=Faulted}为什么task1的状态是Cancelled,而task2的状态是Faulted?请注意,在这两种情况下,我都没有将令牌作为第二个参数传递给Task.Run。这里有两个问题。首先,除了使其可用于任务的lambda之外,将CancellationToken传递给Task.RunAPI始终是个好主意。这样做会将令牌与任务相关联,并且对于正确传播由token.ThrowIfCancellationRequested触发的取消至关重要。但是,这并不能解释为什么task1.Status==TaskStatus.Canceled的取消状态仍然正确传播(task1.Status==TaskStatus.Canceled),但不是task2(task2.Status==TaskStatus.Faulted)。现在,这可能是一种非常罕见的情况,聪明的C#类型推理逻辑可以违背开发人员的意愿。此处和此处对此进行了详细讨论。所以总而言之,对于Task.Run,??编译器推断出Task.Run的以下覆盖:publicstaticTaskRun(Funcfunction)而不是:publicstaticTaskRun(Actionaction)那是因为task1lambda没有自然的forloop中的代码路径,因此它也可以是Funclambda,尽管它不是异步的并且不返回任何内容。对于编译器来说,这是比Action更有利的选项。然后,这个使用Task.Run的重写相当于:vartask1=Task.Factory.StartNew(newFunc(()=>{for(vari=0;;i++){Thread.Sleep(i);//模拟工作项#itoken.ThrowIfCancellationRequested();}})).Unwrap();Task.Factory.StartNew返回一个Task类型的嵌套任务,它由Unwrap()解包为Task。Task.Run足够聪明,可以在接受Func时自动执行此类展开。展开的promise样式任务从其内部任务正确传播取消状态,由Funclambda作为OperationCanceledExceptionexceptions抛出。这不会发生在task2上,它接受Actionlambda并且不创建任何内部任务。取消不会传播到task2,因为令牌尚未通过Task.Run与task2相关联。最后,这可能是task1所期望的行为(当然不是task2),但我们不想在任何一种情况下在幕后创建嵌套任务。此外,通过在for循环中引入条件中断,可以轻松打破task1的这种行为。task1的正确代码应该是:以上是C#学习教程:CancellationToken.ThrowIfCancellationRequested失败和取消任务的状态都分享了。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注——vartask1=Task。Run(newAction(()=>{for(vari=0;;i++){Thread.Sleep(i);//模拟工作项#itoken.ThrowIfCancellationRequested();}}),token);本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:

最新推荐
猜你喜欢