首页 > 解决方案 > C# 考试 70-483 参考书示例错了吗?(父/子线程)

问题描述

在C# 70-483 Exam Ref的第一版中,示例 1-12 给出了将子任务附加到父任务的示例。我认为这是错误的,并希望有人在继续此假设之前检查我的理解。示例中的代码如下:

using System;
using System.Threading.Tasks;

namespace Chapter1
{
    public static class Program
    {
        public static void Main()
        {
            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 (var i in parentTask.Result)
                {
                    Console.WriteLine(i);
                }
            });

            finalTask.Wait();
        }

    }
}

问题在于Task.Run. MSDN 解释说这不允许附加子任务。我认为这可能是 .NET 版本在出版时(2013 年)的问题,但 MSDN 文章是在本书出版前两年发表的。

这特别棘手,因为如果执行上面的代码,results数组确实会将其值设置为 0、1 和 2。但是,如果 lambda 执行的操作比这更耗时,则不会在该索引处设置任何值数组。

例如,下面的代码results分别将“一”和“二”分配给元素 1 和 2,但元素 0 为空。

using System;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Chapter1
{
    public static class Program
    {
        public static void Main()
        {
            Task<String[]> parent = Task.Run(() =>
            {
                var results = new String[3];

                new Task(() => {
                    SHA256 mySHA256 = SHA256.Create();
                    byte[] messageBytes = Encoding.ASCII.GetBytes("asdf");
                    byte[] hashBytes = mySHA256.ComputeHash(messageBytes);
                    results[0] = BitConverter.ToString(hashBytes).Replace("-", "");
                }, TaskCreationOptions.AttachedToParent).Start();

                new Task(() => results[1] = "one", TaskCreationOptions.AttachedToParent).Start();
                new Task(() => results[2] = "two", TaskCreationOptions.AttachedToParent).Start();

                return results;
            });


            var finalTask = parent.ContinueWith(parentTask =>
            {
                foreach (var i in parentTask.Result)
                {
                    Console.WriteLine(i);
                }
            });

            finalTask.Wait();
        }

    }
}

当我使用任务工厂(示例中未显示)启动父任务时,一切都按预期工作并TaskCreationOptions影响子线程的同步。

所以我的问题是:

  1. 我的理解是否正确,传递TaskCreationOptions.AttachedToParent到下创建的子任务是没有意义的Thread.Run
  2. 我的假设是否正确,即本书的示例仅有效,因为子任务 lambda 只是执行在父线程终止之前恰好完成的分配?

标签: c#.nettask

解决方案


我相信您的两个问题(断言)都是正确的。这篇 MSDN文章似乎很好地涵盖了它。

Re 2,正如文章所说,传递AttachedToParent给一个任务开始时Task.Run()会产生“不可预测的”结果,其中一个就是你观察到的结果。

我应该添加一个细微差别,即“子”任务将在ContinueWith()开始后继续运行,因此它们在相应的Console.WriteLine().


推荐阅读