构造函数可以被抢先一步吗?

时间:2013-09-14 11:34:14

标签: c# multithreading

是否有可能在C#中抢占构造函数?

例如,考虑代码:

public class A
{
    public bool ready = true;

    public A()
    {
        ready = false; // Point #1
        // Other initialization stuff
        ready = true; // Point #2
    }
}

代码中的其他地方,两个线程可以访问A类型的变量,第一个线程调用在#1点占用的构造函数。然后第二个线程测试ready并发现它仍然是真的,因此它做了坏事。

这种情况可能吗?

更具体地说:

  1. 可以抢占构造函数吗?
  2. 如果是这样,这是否意味着构造函数中应该有lock等同步代码?
  3. 构造函数退出后,构造的对象是否仅分配给共享变量,从而完全避免了这个问题?

2 个答案:

答案 0 :(得分:5)

我不认为接受的答案是正确的。伊戈尔·奥斯特罗夫斯基的“The C# Memory Model in Theory and Practice”(part 2 here)彻底解释了这些问题,其中一个例子说明了你所询问的内容。

它提供代码

class BoxedInt2
{
  public readonly int _value = 42;
  void PrintValue()
  {
    Console.WriteLine(_value);
  }
}

class Tester
{
  BoxedInt2 _box = null;
  public void Set() {
    _box = new BoxedInt2();
  }
  public void Print() {
    var b = _box;
    if (b != null) b.PrintValue();
  }
}

并注意到:

  

由于BoxedInt实例未正确发布(通过非易失性字段_box),调用Print的线程可能会观察到部分构造的对象!再次,使_box字段不稳定将解决问题。

我强烈鼓励阅读整篇文章,这是非常有用的阅读。

答案 1 :(得分:4)

  

正在构造的对象是否仅分配给共享变量   在构造函数退出之后,从而完全避免了这个问题?

的。仅当构造函数返回时才构造对象,然后将引用的值赋给变量。因此,只有在此之后,其他线程才能调用以检查值是true还是false

两个线程无法同时进入构造函数。

安全发布问题 - 发布未完全创建的对象

但是,如果要发布对某些共享列表的当前引用,则很容易出错。 注意不要在构造函数中执行类似的操作

A(List sharedList){
    sharedList.add(this);

    //initializing instance variables
}

Pardon for Java code。因此,在这种情况下,您将未完成的已创建对象发布到共享列表,该列表可由其他线程访问,并可能导致许多问题。

Don't publish the "this" reference during construction, Java。不确定是否同样适用于C#

  

然后这让我回到原来的问题,在C#中就可以了   构造函数被抢占了吗?

就抢占而言 IMO 线程可以在构造函数运行时被中断 。构造函数就像一个普通的方法,但具有特殊的语义。

根本不会影响代码的线程安全,因为只有一个线程可以在对象的构造函数中。所以它完全是线程安全的,直到你没有逃避未完全构建的this引用。

  

如果是这样,这是否意味着应该有同步代码   作为构造函数中的锁?

构造函数中不仅可以包含单个线程,因此它非常线程安全