有两个线程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
是非易失性字段,Vec2
是具有一些初始化的引用类型:
class Vec2
{
public double x;
public double y;
public Vec2()
{
// this one is important
x = 56.0;
y = 78.0
}
}
问题是,根据C#语言规范(或/和.NET运行时规范),可能会
线程B会观察v
未初始化?也就是说,可能是因为线程B将在例如v
之前观察到对x = 56.0
的引用分配。 volatile
?
编辑:如果是这样,是否会添加v
以确保线程B仅在完全初始化后才会观察x
?
编辑:如果使用字段初始化程序初始化y
,class Vec2
{
public double x = 56.0;
public double y = 78.0;
}
,答案会更改,即:
{{1}}
编辑:我不想在这里解决任何问题,只是想知道实际会发生什么。谢谢你的回答!
答案 0 :(得分:2)
问题是,根据C#语言规范(或/和.NET运行时规范),可能是线程B会观察到v未初始化?
线程B将无法在构造函数运行之前访问v
中的字段,但它可能会"参见" 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
编辑:如果是这样,添加volatile会确保线程B只有在完全初始化后才会观察到v?
易失性不是正确的解决方案。如果需要等待初始化,则应使用同步机制,如上面详述的那样。
编辑:如果使用字段初始值设定项初始化x,y,答案会改变,即:
没有。构造函数和字段初始化程序都将在v
生成"设置"之前运行,但如果没有某些v
,null
将不会{{1}}其他同步到位。