为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
幕布斯6054654 2023-06-25 13:54:41
我遇到了一种情况,我不确定为什么我在多个线程中没有从属性中读取正确的属性值。我编写了一个小型控制台应用程序来说明这种情况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: Thread13:4: Thread25: Thread2我的期望是“3:”应该始终显示“3:Thread1”,因为同一对象已较早更新。但事实并非如此,我不确定我在这里缺少什么......有人可以解释一下吗?只是一个小注意...如果您运行代码并得到不同的顺序,请重新运行它...我只对上面的顺序感兴趣。
查看完整描述

1 回答

?
慕田峪7331174

TA贡献1828条经验 获得超13个赞

有几件事:

  1. 您的Update()方法将写入同步属性MyValue,但对读取不执行任何操作。因此,运行时可以自由地使用对象的缓存值。这不太可能与您观察到的输出有任何关系,因为实际上您不太可能看到这一点,尤其是在 x86 硬件上。但…

  2. 更重要的是,您的代码所展示的只是输出顺序可能会产生误导。该类Console具有同步功能以确保来自多个线程的一致输出,但是代码中没有任何内容可以确保如果按特定顺序写入输出行,则导致这些输出行的代码(例如读取属性)MyValue发生与这些输出行的显示顺序相同。

换句话说,仅仅因为控制台"2:"在该行之前显示了该行"3:",实际上并不意味着对的调用发生Update("Thread1")在对Console.WriteLine("3: " + obj.MyValue);

lock如果您想确保输出行与程序中语句的执行顺序相匹配,您还需要使用语句来保护各个操作。

更具体地说,请考虑以下可能的代码执行顺序:

thread 1                                      thread 2

--------                                      --------

                                              "value" parameter <= "3: " + obj.MyValue

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

Update("Thread1");

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

                                              Console.WriteLine(value);

Console.WriteLine()即,在第一个线程开始其逻辑之前,第二个线程计算传递给的参数是完全合法的。但是,第一个线程也可以在第二个线程有机会实际调用该Console.WriteLine()方法之前抢占第二个线程。


如果发生这种情况,您会看到线程 1 的预期输出顺序,但线程 2 的输出写入该属性的明显过时版本MyValue,因为它是在线程 1 运行其任何逻辑之前检索到的。


为了解决该特定场景,您可以使用lock呼叫WriteLine()。例如:


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

请注意,您需要将其放在每次调用Console.WriteLine(). 这将确保写入控制台的任何值都是调用时属性的最新值。


查看完整回答
反对 回复 2023-06-25
  • 1 回答
  • 0 关注
  • 67 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信