简化了问题,以便更清楚地表达我实际要求的内容
我有两个主题,称之为A
和B
。它们共享一个Foo
类型的对象,该对象具有一个名为Name
的字段,并存储在索引为Foo[]
的{{1}}类型的数组中。线程将始终按照系统已经保证的顺序访问索引0
,因此线程0
之前没有线程B
的竞争条件。
顺序就是这样。
A
正如我所说,这个顺序已经得到保证,没有办法让线程B在线程A之前读取值
我想要确保的是两件事:
// Thread A
array[0].Name = "Jason";
// Thread B
string theName = array[0].Name
始终获取.Name字段中的最新值将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
上执行VolatileRead
或MemoryBarrier
是否会确保索引0
处对象中的所有字段都获得最新值?
答案 0 :(得分:2)
这些解决方案lock
或volatile
都无法解决您的问题。这是因为:
volatile
确保由一个线程更改的变量对于在相同数据上运行的其他线程(即它们未被缓存)立即可见,并且对该变量的操作不会重新排序。不是你真正需要的。lock
确保写入/读取不会同时发生,但不保证其顺序。它取决于哪个线程首先获得锁定,这是不确定的。因此,如果你的流程是:
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)上,它通常很重要。