这三种任务继续方式有什么区别?

3

我有这两个场景,但我不理解为什么事情会按照它们发生:

    static void Main(string[] args)
    {
        Console.WriteLine("***Starting T1");
        //run two tasks sequentially
        Task t = FirstTask().ContinueWith(_ => SecondTask(), TaskContinuationOptions.OnlyOnRanToCompletion);
        //register succeded and faulted continuations
        t.ContinueWith(_ => Completion(), TaskContinuationOptions.OnlyOnRanToCompletion);
        t.ContinueWith(_ => Faulted(), TaskContinuationOptions.OnlyOnFaulted);
        Console.ReadLine();
        Console.WriteLine("***Starting T2");
        Task t2 = FirstTask().ContinueWith(_ => FaultTask(), TaskContinuationOptions.OnlyOnRanToCompletion);
        t2.ContinueWith(_ => Completion(), TaskContinuationOptions.OnlyOnRanToCompletion);
        t2.ContinueWith(_ => Faulted(), TaskContinuationOptions.OnlyOnFaulted);
        Console.ReadLine();
        Console.WriteLine("***Starting T3");
        Task t3 = FirstTask().ContinueWith(ant => ant.ContinueWith(_ => FaultTask(), TaskContinuationOptions.OnlyOnRanToCompletion));
        t3.ContinueWith(_ => Completion(), TaskContinuationOptions.OnlyOnRanToCompletion);
        t3.ContinueWith(_ => Faulted(), TaskContinuationOptions.OnlyOnFaulted);
        Console.ReadLine();
    }

    private static Task FirstTask()
    {
        return Task.Run(() =>
        {
            Console.WriteLine("Task 1");
            Thread.Sleep(1000);
        });
    }

    private static Task SecondTask()
    {
        return Task.Run(() =>
        {
            Console.WriteLine("Task 2");
            Thread.Sleep(1000);
        });
    }

    private static Task FaultTask()
    {
        return Task.Run(() =>
        {
            Console.WriteLine("Throw...");
            Thread.Sleep(1000);
            throw new ArgumentException();
        });
    }

    private static void Completion()
    {
        Console.WriteLine("Complete");
    }

    private static void Faulted()
    {
        Console.WriteLine("Faulted");
    }

在第一种情况下,所有任务都按预期运行。但是,如果在 FirstTask() 中移除 Sleep(),则不能保证按顺序运行任务。
在第二种情况下,Faulted() 处理程序不会运行。我希望它发生,因为出现了未处理的异常。
在第三种情况下,异常是在运行 Complete() 处理程序后抛出的。我对这种顺序感到困惑。
基本上,我想能够链式连接任意数量的任务,并在前一个任务完成后按顺序运行它们。创建链之后,我将显示等待窗体,并在最终任务(即所有任务完成后)中注册 OnlyOnRanToCompletionOnlyOnCancelledOnlyOnFaulted 的连续项以关闭窗体-显示成功或错误。
这是MSDN所提到的那些选项,它们不适用于多任务连续项的地方吗?

3
两个任务都“运行完成”。投票关闭,因为无法重现。一旦您使用可以重现问题的代码更新问题,我将撤回投票。我怀疑您的原始代码将设置一些延续标志,而您没有向我们展示它们。 - Sriram Sakthivel
你的代码仍然没有展现出所描述的行为。请在发布之前运行代码并进行测试。谢谢。 - Sriram Sakthivel
请问您能否发布所需的代码以演示问题,这样我就可以将代码复制并粘贴到控制台应用程序中运行?目前第一个任务甚至无法编译。 - Enigmativity
@Simon,每当你调用Task.Run()时,你的任务已经开始了。因此,例如,在代码的第一行中,你同时启动了两个任务。 - Zruty
啊,是的,谢谢Zruty。 - Simon
1个回答

2

你在 tt2 上的返回类型是 Task<Task>,而不仅仅是一个 Task。T3 是一个 Task<Task<Task>>。为了获得所需的行为,你应该能够 解包 这些任务,这将给你一个代表整个操作的任务(阅读文档以获取更多信息):

Console.WriteLine("***Starting T1");
//run two tasks sequentially
Task<Task> t = FirstTask().ContinueWith(_ => SecondTask(), TaskContinuationOptions.OnlyOnRanToCompletion);

//register succeded and faulted continuations
t.Unwrap().ContinueWith(_ => Completion(), TaskContinuationOptions.OnlyOnRanToCompletion);
t.Unwrap().ContinueWith(_ => Faulted(), TaskContinuationOptions.OnlyOnFaulted);

Console.ReadLine();
Console.WriteLine("***Starting T2");

Task<Task> t2 = FirstTask().ContinueWith(_ => FaultTask(), TaskContinuationOptions.OnlyOnRanToCompletion);
t2.Unwrap().ContinueWith(_ => Completion(), TaskContinuationOptions.OnlyOnRanToCompletion);
t2.Unwrap().ContinueWith(_ => Faulted(), TaskContinuationOptions.OnlyOnFaulted);
Console.ReadLine();

我建议尽可能使用async/await模式,因为它可以使处理这些任务变得更加容易。

谢谢,是的我明白了。通常我可以使用:await t1; await t2; await t3;,但是这样检查任务完成状态会更加麻烦。 - Simon
@Simon,你为什么认为这更麻烦?它肯定更易读。很可能,你认为使用async/await方法更麻烦的问题可以通过一个或两个辅助方法来解决。 - Matt Smith
如果只有在第一个任务成功运行后才执行t2,或者只有在第一个任务失败后才执行t3,那么解决方案在我看来不够易读。你需要持有一个对Task的引用,然后执行一个if语句。 - Simon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接