【发布时间】:2012-01-11 14:56:54
【问题描述】:
我经常听说在 .NET 2.0 内存模型中,写入总是使用释放栅栏。这是真的?这是否意味着即使没有显式的内存屏障或锁,也不可能在与创建对象不同的线程上观察到部分构造的对象(仅考虑引用类型)?我显然排除了构造函数泄漏this 引用的情况。
例如,假设我们有不可变的引用类型:
public class Person
{
public string Name { get; private set; }
public int Age { get; private set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
是否可以使用以下代码观察除“John 20”和“Jack 21”以外的任何输出,例如“null 20”或“Jack 0”?
// We could make this volatile to freshen the read, but I don't want
// to complicate the core of the question.
private Person person;
private void Thread1()
{
while (true)
{
var personCopy = person;
if (personCopy != null)
Console.WriteLine(personCopy.Name + " " + personCopy.Age);
}
}
private void Thread2()
{
var random = new Random();
while (true)
{
person = random.Next(2) == 0
? new Person("John", 20)
: new Person("Jack", 21);
}
}
这是否也意味着我可以将所有共享字段设为深度不可变引用类型volatile 并且(在大多数情况下)继续我的工作?
【问题讨论】:
-
我看不出内存模型对这个问题有什么影响。线程 2 只分配完全构造的 Person 对象,那么你怎么能观察到部分构造的对象呢?
-
@Peter Ruderman 因为内存模型说你可以(或不能)
-
因为如果构造函数在构造完成之前返回一个指向正在构造的对象的指针,您可以看到一半生成的对象。如果 cpu 无序执行,则很有可能在没有内存屏障的情况下发生这种情况。我很确定 .NET 保证不会,但我没有证据 :)
-
@Peter Ruderman 您假设数据已分配并且代码按照您在源代码中看到的顺序执行,这在现代 CPU 上可能不是这种情况(当然也不是在非缓存一致的系统,尽管 .NET 尚未在此类系统上运行 afaik)
-
@Boris:引用类型分配的原子性只能防止引用本身(它只是一个托管指针)的读/写撕裂。
标签: c# .net multithreading thread-safety memory-model