对象数组,在一个线程中更新并在另一个线程中读取

时间:2011-12-18 11:00:16

标签: c# .net multithreading mono thread-safety

简化了问题,以便更清楚地表达我实际要求的内容

我有两个主题,称之为AB。它们共享一个Foo类型的对象,该对象具有一个名为Name的字段,并存储在索引为Foo[]的{​​{1}}类型的数组中。线程将始终按照系统已经保证的顺序访问索引0,因此线程0之前没有线程B的竞争条件。

顺序就是这样。

A

正如我所说,这个顺序已经得到保证,没有办法让线程B在线程A之前读取值

我想要确保的是两件事:

  1. 两个线程都在索引0处获取最新对象。
  2. 线程 // Thread A array[0].Name = "Jason"; // Thread B string theName = array[0].Name 始终获取.Name字段中的最新值
  3. B标记为易失性不是一种选择,因为真实对象要复杂得多,甚至还有自定义结构甚至不能附加volatile属性。

    现在,满足1很容易(总是得到最新的对象),你可以做一个.VolatileRead:

    Name

    或者您可以插入内存屏障:

     // Thread A
     Foo obj = (Foo)Thread.VolatileRead(ref array[0]);
     obj.Name = "Jason";
    
     // Thread B
     Foo obj = (Foo)Thread.VolatileRead(ref array[0]);
     string theName = obj.Name
    

    所以我的问题是:这还足以满足条件2吗?我总是从我读出的对象的字段中获得最新值?如果索引 // Thread A array[0].Name = "Jason"; Thread.MemoryBarrier(); // Thread B Thread.MemoryBarrier(); string theName = array[0].Name 处的对象未更改,但0已更改。在索引Name上执行VolatileReadMemoryBarrier是否会确保索引0处对象中的所有字段都获得最新值?

2 个答案:

答案 0 :(得分:2)

这些解决方案lockvolatile都无法解决您的问题。这是因为:

  1. volatile确保由一个线程更改的变量对于在相同数据上运行的其他线程(即它们未被缓存)立即可见,并且对该变量的操作不会重新排序。不是你真正需要的。
  2. lock确保写入/读取不会同时发生,但不保证其顺序。它取决于哪个线程首先获得锁定,这是不确定的。
  3. 因此,如果你的流程是:

    Thread A read Name
    Thread A modify Name
    Thread B read Name
    

    完全按照该顺序,您需要使用事件强制执行(例如AutoresetEvent):

    //Thread A
    foo[0].Name = "John"; // write value
    event.Set(); // signal B that write is completed
    
    //Thread B
    event.WaitOne(); // wait for signal
    string name = foo[0].Name; // read value
    

    这保证了线程B在A修改它之前不会读取Name变量。

    修改:好的,所以您确定上述流程得到了遵守。既然你说你不能声明字段volatile,我建议使用Thread.MemoryBarrier()来引入强制排序的围栏:

    //Thread A
    foo[0].Name = "John"; // write value
    Thread.MemoryBarrier();
    
    //Thread B
    Thread.MemoryBarrier();
    string name = foo[0].Name; // read value
    

    有关详细信息,请查看此文档:http://www.albahari.com/threading/part4.aspx

答案 1 :(得分:0)

如果我理解正确,锁定将解决您遇到的问题。这是因为锁会在自身周围产生隐含的(完整的)内存障碍。

您还可以使用Thread.MemoryBarrier显式使用内存屏障。阅读更多here。在x86上很难注意到内存屏障效应,但是在更加宽松的订购系统(如PPC)上,它通常很重要。