【问题标题】:Task hierarchy example not working as expected任务层次结构示例未按预期工作
【发布时间】:2013-10-07 12:55:58
【问题描述】:

我开始在 C# 中使用任务。 我正在尝试执行此代码。

    private static void CreateSubtask() {

        Task<Int32[]> parent = Task.Run(() =>
        {
            var results = new Int32[3];
            new Task(() => results[0] = 0,
                TaskCreationOptions.AttachedToParent).Start();
            new Task(() => results[1] = 1,
                TaskCreationOptions.AttachedToParent).Start();
            new Task(() => results[2] = 2,
                TaskCreationOptions.AttachedToParent).Start();
            return results;
        });
        var finalTask = parent.ContinueWith(
           parentTask =>
           {
               foreach (int i in parentTask.Result)
                   Console.WriteLine(i);
           });
        finalTask.Wait();
    }

finalTask 仅在父 Task 完成后运行,而父 Task 在所有三个子节点都完成后才运行。您可以使用它来创建相当复杂的 Task 层次结构,这些层次结构将执行您指定的所有步骤。

我从执行中得到的是三行内容:

0
0
0

我期待他们成为

0
1
2

我说的对吗?

【问题讨论】:

  • 我不熟悉任务并行库,但我猜您在任务parent 中创建的三个任务在您从parent 阅读时尚未运行结果。似乎您在等待parent 完成,而不是等待在parent 内创建的三个任务。
  • 我运行了你的代码,得到 0 1 2 这是我所期望的

标签: c# parallel-processing task-parallel-library


【解决方案1】:

将Task.Run 与父任务一起使用会抑制AttachedToParent 对子任务的影响:

Task.Run vs Task.Factory.StartNew by Stephen Toub。

请改用Task.Factory.StartNew。

【讨论】:

  • 来自链接的文章:“[Task.Run] 应该被简单地认为是使用 Task.Factory.StartNew 的一种快速方法”。在文章的后面,它详细说明了传递给底层 StartNew 的参数包括 TaskCreationOptions.DenyChildAttach。
【解决方案2】:

问题是您的parent 任务在完成开始其他三个任务时完成,而不是在其他三个任务完成时完成。

相反,您可以使用Task.WhenAll 创建一个将在所有其他任务本身完成时完成的任务。

另一个使您的程序更符合惯用任务代码的更改是让每个内部任务返回它们自己的值,而不是改变某些共享状态,这仅仅是因为在多线程环境中处理共享状态可能更难推理。

var parent = Task.WhenAll(Task.Run(() => 0),
    Task.Run(() => 1),
    Task.Run(() => 2));
var finalTask = parent.ContinueWith(t =>
    {
        foreach (int n in t.Result)
            Console.WriteLine(n);
    });
finalTask.Wait();

【讨论】:

    【解决方案3】:

    您只是开始您的三个子任务,而不是等待它们完成。像这样调整它,例如:

                var task1 = new Task(() => results[0] = 0,
                    TaskCreationOptions.AttachedToParent);
                var task2 = new Task(() => results[1] = 1,
                    TaskCreationOptions.AttachedToParent);
                var task3 = new Task(() => results[2] = 2,
                    TaskCreationOptions.AttachedToParent);
    
                task1.Start();
                task2.Start();
                task3.Start();
    
                task1.Wait();
                task2.Wait();
                task3.Wait();
    

    还请注意,使用您当前的代码,仍然可以显示0 1 2(顺便说一下,不是1 2 3),因为无法确定子任务何时运行/完成。这也可能取决于您的构建配置(调试/发布)。

    【讨论】:

    • AttachedToParent 通常让父母等待它的孩子。问题是,为什么不在这里?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    相关资源
    最近更新 更多