C#考试70-483参考书示例是否错误? (父/子线程)

时间:2018-08-12 20:44:43

标签: c# .net task

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 explains这样做 not 不允许附加子任务。我认为这可能是发布(2013)时.NET版本的问题,但是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只是执行在父线程终止之前完成的分配而起作用?

1 个答案:

答案 0 :(得分:1)

我相信您的两个问题(断言)都是正确的。 This MSDN的文章似乎涵盖得很好。

Re 2,如文章所述,将AttachedToParent传递给以Task.Run()开头的任务会产生“不可预测的”结果,其中之一就是您观察到的结果。

我应该添加一个细微差别,即“ {child}”任务将在ContinueWith()开始之后继续运行,因此它们会花一点时间完成相应的Console.WriteLine()之前