AttachedToParent任务混乱

 断肠人blogma_791 发布于 2023-02-13 20:32

我在理解AttachedToParent参数如何工作时遇到了问题.

以下是示例代码:

public static void Main(string[] args)
    {
        Task 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();
    }

据我所知,当一个Task有子任务时,父任务在所有子任务都准备就绪时结束.此示例的问题是输出如下所示:

0
0
0

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

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

我的问题是这个.为什么TaskCreationOptions.AttachedToParent我们还必须为每个子任务手动调用Wait方法?

编辑:

当我写这个问题的时候,我已经改变了一点代码,现在AttachedToParent工作得很好.唯一的区别是我用过parentTask.Start();而不是用Task.Run();.

public static void Main(string[] args)
    {
        Task parentTask = new Task(()=> 
        {
            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();
    }

我仍然不明白为什么第一个例子有问题.

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.StartNewTaskCreationOptions creationOptions.在Task.Factory.StartNew参数等于的方法中TaskCreationOptions.DenyChildAttach.这意味着

    如果尝试将子任务附加到创建的任务,则将引发InvalidOperationException

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

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

    2023-02-13 20:34 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有