首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >CancellationToken.ThrowIfCancellationRequested之后的Faulted vs Cancelled任务状态

CancellationToken.ThrowIfCancellationRequested之后的Faulted vs Cancelled任务状态
EN

Stack Overflow用户
提问于 2018-05-02 09:12:37
回答 1查看 0关注 0票数 0

下面的代码开始并等待两个任务,task1task2几乎完全相同。task1不同于task2的是它运行一个永无止境的循环。这两种情况对于执行CPU绑定工作的一些现实生活场景都非常典型。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    public class Program
    {
        static async Task TestAsync()
        {
            var ct = new CancellationTokenSource(millisecondsDelay: 1000);
            var token = ct.Token;

            // start task1
            var task1 = Task.Run(() =>
            {
                for (var i = 0; ; i++)
                {
                    Thread.Sleep(i); // simulate work item #i
                    token.ThrowIfCancellationRequested();
                }
            });

            // start task2
            var task2 = Task.Run(() =>
            {
                for (var i = 0; i < 1000; i++)
                {
                    Thread.Sleep(i); // simulate work item #i
                    token.ThrowIfCancellationRequested();
                }
            });  

            // await task1
            try
            {
                await task1;
            }
            catch (Exception ex)
            {
                Console.WriteLine(new { task = "task1", ex.Message, task1.Status });
            }

            // await task2
            try
            {
                await task2;
            }
            catch (Exception ex)
            {
                Console.WriteLine(new { task = "task2", ex.Message, task2.Status });
            }
        }

        public static void Main(string[] args)
        {
            TestAsync().Wait();
            Console.WriteLine("Enter to exit...");
            Console.ReadLine();
        }
    }
}

输出:

{ task = task1, Message = The operation was canceled., Status = Canceled }
{ task = task2, Message = The operation was canceled., Status = Faulted }

为什么状态task1Cancelled,但状态task2Faulted请注意,在这两种情况下,我都不会将其token作为第二个参数传递给Task.Run

EN

回答 1

Stack Overflow用户

发布于 2018-05-02 18:20:55

这里有两个问题。首先,传递CancellationTokenTask.RunAPI 总是一个好主意,除了可用于任务的lambda。这样做会将令牌与任务关联起来,对于正确传播由所触发的取消操作至关重要token.ThrowIfCancellationRequested

然而,这并不能解释为什么取消状态task1仍然正确地传播(task1.Status == TaskStatus.Canceled),而不是task2task2.Status == TaskStatus.Faulted)。

现在,这可能是其中巧妙的C#类型推理逻辑可能违背开发者意愿的极少数情况之一。这里这里详细讨论。总而言之,在task1以下情况下,Task.Run编译器推断以下重写:

public static Task Run(Func<Task> function)

而不是:

public static Task Run(Action action)

这是因为task1lambda没有自然的代码路径for,所以它可能是一个Func<Task>lambda,尽管它不是async,它不会返回任何东西。这是编译器更喜欢的选项Action。然后,这种覆盖的使用Task.Run等同于:

var task1 = Task.Factory.StartNew(new Func<Task>(() =>
{
    for (var i = 0; ; i++)
    {
        Thread.Sleep(i); // simulate work item #i
        token.ThrowIfCancellationRequested();
    }
})).Unwrap();

最后,这可能是一个理想的行为task1(当然不是task2),但我们不希望在任何情况下在场景后面创建嵌套任务。而且,task1通过breakfor循环中引入一个条件,这种行为很容易被破坏。

正确的代码task1应该是这样的

var task1 = Task.Run(new Action(() =>
{
    for (var i = 0; ; i++)
    {
        Thread.Sleep(i); // simulate work item #i
        token.ThrowIfCancellationRequested();
    }
}), token);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/-100008329

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档