C#不是线程安全的初始化

时间:2015-03-13 19:59:10

标签: c# multithreading volatile

有两个线程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

编辑:如果使用字段初始化程序初始化yclass Vec2 { public double x = 56.0; public double y = 78.0; } ,答案会更改,即:

{{1}}

编辑:我不想在这里解决任何问题,只是想知道实际会发生什么。谢谢你的回答!

1 个答案:

答案 0 :(得分:2)

  

问题是,根据C#语言规范(或/和.NET运行时规范),可能是线程B会观察到v未初始化?

线程B将无法在构造函数运行之前访问v中的字段,但它可能会"参见" vnull,具体取决于线程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生成"设置"之前运行,但如果没有某些vnull将不会{{1}}其他同步到位。