在多线程程序中未读取正确的属性值

我遇到了一种情况,我不确定为什么我在多个线程中没有从属性中读取正确的属性值。我编写了一个小型控制台应用程序来说明这种情况


class Program

{

    private static Test MyObject;

    static void Main(string[] args)

    {

        MyObject = new Test();


        Task.Run(() =>

        {

            var obj = MyObject as Test;

            Console.WriteLine("1: " + obj.MyValue);

            Update("Thread1");

            Console.WriteLine("2: " + obj.MyValue);

        });

        Task.Run(() =>

        {

            var obj = MyObject as Test;

            Console.WriteLine("3: " + obj.MyValue);

            Thread.Sleep(100);

            Update("Thread2");

            Console.WriteLine("4: " + obj.MyValue);

        });


        void Update(string val)

        {

            lock (MyObject)

            {

                MyObject.MyValue = val;

            }

        }


        Thread.Sleep(400);

        Console.WriteLine("5: " + MyObject.MyValue);

    }

}


class Test

{

    public string MyValue { get; set; }

}

运行上面的行我得到这个输出


1:

2: Thread1

3:

4: Thread2

5: Thread2

我的期望是“3:”应该始终显示“3:Thread1”,因为同一对象已较早更新。但事实并非如此,我不确定我在这里缺少什么......有人可以解释一下吗?


只是一个小注意...如果您运行代码并得到不同的顺序,请重新运行它...我只对上面的顺序感兴趣。


幕布斯6054654
浏览 70回答 1
1回答

慕田峪7331174

有几件事:您的Update()方法将写入同步到属性MyValue,但对读取不执行任何操作。因此,运行时可以自由地使用对象的缓存值。这不太可能与您观察到的输出有任何关系,因为实际上您不太可能看到这一点,尤其是在 x86 硬件上。但…更重要的是,您的代码所展示的只是输出顺序可能会产生误导。该类Console具有同步功能以确保来自多个线程的一致输出,但是代码中没有任何内容可以确保如果按特定顺序写入输出行,则导致这些输出行的代码(例如读取属性)MyValue发生与这些输出行的显示顺序相同。换句话说,仅仅因为控制台"2:"在该行之前显示了该行"3:",实际上并不意味着对的调用发生Update("Thread1")在对Console.WriteLine("3: " + obj.MyValue);lock如果您想确保输出行与程序中语句的执行顺序相匹配,您还需要使用语句来保护各个操作。更具体地说,请考虑以下可能的代码执行顺序:thread 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; thread 2--------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; --------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "value" parameter <= "3: " + obj.MyValueConsole.WriteLine("1: " + obj.MyValue);Update("Thread1");Console.WriteLine("2: " + obj.MyValue);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine(value);Console.WriteLine()即,在第一个线程开始其逻辑之前,第二个线程计算传递给的参数是完全合法的。但是,第一个线程也可以在第二个线程有机会实际调用该Console.WriteLine()方法之前抢占第二个线程。如果发生这种情况,您会看到线程 1 的预期输出顺序,但线程 2 的输出写入该属性的明显过时版本MyValue,因为它是在线程 1 运行其任何逻辑之前检索到的。为了解决该特定场景,您可以使用lock呼叫WriteLine()。例如:lock (MyObject) Console.WriteLine("1: " + obj.MyValue);请注意,您需要将其放在每次调用Console.WriteLine(). 这将确保写入控制台的任何值都是调用时属性的最新值。
打开App,查看更多内容
随时随地看视频慕课网APP