【问题标题】:AttachedToParent Task confusionAttachedToParent 任务混淆
【发布时间】:2013-12-15 05:48:48
【问题描述】:

我无法理解 AttachedToParent 参数的工作原理。

这里是示例代码:

public static void Main(string[] args)
    {
        Task<int[]> parentTask = Task.Run(()=> 
        {
            int[] results = new int[3];

            Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent);
            Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent);
            Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent);

            t1.Start();
            t2.Start();
            t3.Start();

            return results;
        });

        Task finalTask = parentTask.ContinueWith(parent =>
        {
            foreach (int result in parent.Result)
            {
                Console.WriteLine(result);
            }
        });

        finalTask.Wait();
        Console.ReadLine();
    }

据我了解,当一个任务有子任务时,当所有子任务都准备好时,父任务完成。这个例子的问题是输出看起来像这样:

0
0
0

这意味着父任务没有等待其子任务完成。获得有效结果 0 1 2 的唯一方法是在所有子 Taks 上使用 Wait,方法是在 return results; 语句之前添加一些类似这样的代码:

Task[] taskList = { t1, t2, t3 };
Task.WaitAll(taskList);

我的问题是这样的。当我们还必须为每个子任务手动调用 Wait 方法时,为什么要使用TaskCreationOptions.AttachedToParent

编辑:

在我写这个问题的时候,我稍微修改了代码,现在 AttachedToParent 运行良好。唯一的区别是我使用了parentTask.Start(); 而不是Task.Run();

public static void Main(string[] args)
    {
        Task<int[]> parentTask = new Task<int[]>(()=> 
        {
            int[] results = new int[3];

            Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent);
            Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent);
            Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent);

            t1.Start();
            t2.Start();
            t3.Start();

            //Task[] taskList = { t1, t2, t3 };
            //Task.WaitAll(taskList);

            return results;
        });

        parentTask.Start();

        Task finalTask = parentTask.ContinueWith(parent =>
        {
            foreach (int result in parent.Result)
            {
                Console.WriteLine(result);
            }
        });

        finalTask.Wait();
        Console.ReadLine();
    }

我还是不明白为什么第一个例子有问题。

【问题讨论】:

    标签: c# multithreading task-parallel-library console-application


    【解决方案1】:

    看这篇博文:Task.Run vs Task.Factory.StartNew

    第一个例子:

    Task.Run(someAction);
    

    是方法的简化等价物:

    Task.Factory.StartNew(someAction,
             CancellationToken.None,
             TaskCreationOptions.DenyChildAttach,
             TaskScheduler.Default);
    

    我做了一点研究,使用反射器,这里是方法的来源Task.Run

    public static Task Run(Func<Task> function, CancellationToken cancellationToken)
        {
          if (function == null)
            throw new ArgumentNullException("function");
          cancellationToken.ThrowIfSourceDisposed();
          if (cancellationToken.IsCancellationRequested)
            return Task.FromCancellation(cancellationToken);
          else
            return (Task) new UnwrapPromise<VoidTaskResult>(
                (Task) Task<Task>.Factory.StartNew(function,
                                      cancellationToken, 
                                      TaskCreationOptions.DenyChildAttach,
                                      TaskScheduler.Default),
                true);
        }
    

    方法Task.Factory.StartNew的重要参数是TaskCreationOptions creationOptions。在方法Task.Factory.StartNew 中,该参数等于TaskCreationOptions.DenyChildAttach。这意味着

    如果尝试 将子任务附加到创建的任务

    您需要更改为TaskCreationOptions.None 才能实现代码的正确行为。

    方法Task.Run 不提供更改TaskCreationOptions 参数的能力。

    【讨论】:

    • DenyChildAttach 似乎确实是问题所在。当我像这样创建父任务时:TaskFactory tf = new TaskFactory(TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None); Task&lt;int[]&gt; parentTask = tf.StartNew(() =&gt; {....}); 我遇到了同样的问题,当我使用 TaskCreationOptions.None 时它工作正常。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-09
    • 2017-06-09
    • 1970-01-01
    • 2018-11-11
    • 1970-01-01
    相关资源
    最近更新 更多