C# 不是线程安全的初始化
C# not thread-safe initialization
有两个线程A和B,线程A执行如下赋值:
v = new Vec2()
线程B使用v
,例如:
double length = Math.Sqrt(v.x*v.x + v.y*v.y);
Console.WriteLine("length is {0}", length);
v
是一个 non-volatile 字段,Vec2
是一个带有一些初始化的引用类型:
class Vec2
{
public double x;
public double y;
public Vec2()
{
// this one is important
x = 56.0;
y = 78.0
}
}
问题是,根据 C# 语言规范(or/and .NET 运行时规范),可能
是不是线程 B 会观察到 v
未初始化?也就是说,线程 B 可能会在例如之前观察到对 v
的引用赋值。 x = 56.0
?
编辑:如果是这样,添加volatile
是否会确保线程B仅在完全初始化后才观察到v
?
编辑:如果x
、y
使用字段初始化器初始化,答案会改变吗,即:
class Vec2
{
public double x = 56.0;
public double y = 78.0;
}
编辑:我在这里不寻求解决任何问题,只是想知道实际会发生什么。感谢您的回答!
The question is, according to C# language spec (or/and .NET runtime spec), might it be that the thread B will observe v uninitialized?
线程 B 将无法在构造函数 运行 之前访问 v
内的字段,但它可能 "see" v
为 null
,取决于线程 A 中的初始化发生的时间以及初始化和线程 B 访问之间是否存在内存屏障。
一般来说,如果你有两个线程,并且你需要按顺序发生某些事情(在这种情况下,v
在线程 B 中使用之前由线程 A 初始化),你将需要显式添加某种形式的同步。
对于您的情况,像等待句柄这样简单的东西就足够了:
// Assuming the existence of this in scope of both threads:
// ManualResetEvent mre = new ManualResetEvent(false)
在线程 A 中:
v = new Vec2();
mre.Set(); // Denote that initialization is complete
在线程 B 中:
mre.WaitOne(); // Wait (blocking) until initialization is complete
double length = Math.Sqrt(v.x*v.x + v.y*v.y);
// .. Other code as needed
EDIT: if so, would adding volatile ensure that thread B observes v only after it is fully initialized?
Volatile 不是正确的解决方案。如果您需要等待初始化发生,您应该使用同步机制,例如上面详述的那个。
EDIT: will the answer change if x,y are initialized using field initializers, that is:
没有。构造函数和字段初始值设定项都将在 v
成为 "set" 之前全部 运行,但你不能保证 v
不会是 null
没有其他一些同步到位。
有两个线程A和B,线程A执行如下赋值:
v = new Vec2()
线程B使用v
,例如:
double length = Math.Sqrt(v.x*v.x + v.y*v.y);
Console.WriteLine("length is {0}", length);
v
是一个 non-volatile 字段,Vec2
是一个带有一些初始化的引用类型:
class Vec2
{
public double x;
public double y;
public Vec2()
{
// this one is important
x = 56.0;
y = 78.0
}
}
问题是,根据 C# 语言规范(or/and .NET 运行时规范),可能
是不是线程 B 会观察到 v
未初始化?也就是说,线程 B 可能会在例如之前观察到对 v
的引用赋值。 x = 56.0
?
编辑:如果是这样,添加volatile
是否会确保线程B仅在完全初始化后才观察到v
?
编辑:如果x
、y
使用字段初始化器初始化,答案会改变吗,即:
class Vec2
{
public double x = 56.0;
public double y = 78.0;
}
编辑:我在这里不寻求解决任何问题,只是想知道实际会发生什么。感谢您的回答!
The question is, according to C# language spec (or/and .NET runtime spec), might it be that the thread B will observe v uninitialized?
线程 B 将无法在构造函数 运行 之前访问 v
内的字段,但它可能 "see" v
为 null
,取决于线程 A 中的初始化发生的时间以及初始化和线程 B 访问之间是否存在内存屏障。
一般来说,如果你有两个线程,并且你需要按顺序发生某些事情(在这种情况下,v
在线程 B 中使用之前由线程 A 初始化),你将需要显式添加某种形式的同步。
对于您的情况,像等待句柄这样简单的东西就足够了:
// Assuming the existence of this in scope of both threads:
// ManualResetEvent mre = new ManualResetEvent(false)
在线程 A 中:
v = new Vec2();
mre.Set(); // Denote that initialization is complete
在线程 B 中:
mre.WaitOne(); // Wait (blocking) until initialization is complete
double length = Math.Sqrt(v.x*v.x + v.y*v.y);
// .. Other code as needed
EDIT: if so, would adding volatile ensure that thread B observes v only after it is fully initialized?
Volatile 不是正确的解决方案。如果您需要等待初始化发生,您应该使用同步机制,例如上面详述的那个。
EDIT: will the answer change if x,y are initialized using field initializers, that is:
没有。构造函数和字段初始值设定项都将在 v
成为 "set" 之前全部 运行,但你不能保证 v
不会是 null
没有其他一些同步到位。