【发布时间】:2011-12-18 11:00:15
【问题描述】:
简化了问题,更清楚地表达了我的实际问题
我有两个线程,分别称为A 和B。它们共享一个Foo 类型的对象,该对象有一个名为Name 的字段,并存储在索引0 的Foo[] 类型数组中。线程将始终按照系统已保证的顺序访问索引0,因此线程B 不会出现在线程A 之前的竞争条件。
顺序是这样的。
// Thread A
array[0].Name = "Jason";
// Thread B
string theName = array[0].Name
正如我所说,这个顺序已经保证了,线程B没有办法读取线程A之前的值
我要确保的是两件事:
- 两个线程都在索引 0 处获取最新对象。
- 线程
B总是获取.Name 字段中的最新值
将Name 标记为 volatile 不是一种选择,因为真实对象要复杂得多,甚至具有自定义结构,甚至无法附加 volatile 属性。
现在,满足 1 很容易(总是获取最新的对象),您可以执行 .VolatileRead:
// 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
或者你可以插入一个内存屏障:
// Thread A
array[0].Name = "Jason";
Thread.MemoryBarrier();
// Thread B
Thread.MemoryBarrier();
string theName = array[0].Name
所以我的问题是:这是否也足以满足条件 2?我总是从我读出的对象的字段中获得最新的值?如果索引0 处的对象没有更改,但Name 已更改。在索引0 上执行VolatileRead 或MemoryBarrier 是否会确保索引0 处的对象中的所有字段也获得它们的最新值?
【问题讨论】:
-
你为什么使用数组?查看
System.Collections.Concurrent命名空间。 -
首先不使用 .NET4。据我所知,并发的东西只保证集合本身的内容,而不是集合中对象的可能字段/属性。
-
我不认为 volatile 能保证你所想的。
-
如果不能保证插入到数组中的对象是线程安全的,我认为你必须在系统中进行某种检查。我可能会通过使用 Concurrent 命名空间中的集合之一来实现它。创建一个具有公共 T 值的 IDisposable 和泛型新类型。每当一个线程处理完该项目时,它都会处理它,此时您将允许其他线程读取它。只是一个想法。
-
另一个想法是只允许线程安全项进入自定义集合。您只需在 List
或 T[] 周围放置一个通用包装器,并限制 T 是 IThreadSafe(它没有任何成员,但作为对您和其他开发人员的提醒)。
标签: c# .net multithreading mono thread-safety