对象分配线程安全吗?

时间:2011-10-06 17:13:46

标签: c# multithreading thread-safety

这个线程安全吗?具体来说,GetMyObject()方法是否可以返回null?我知道两个线程有​​可能获得MyObject的不同实例,但我并不关心。 我只是想确保假设GetMyObject()永远不会返回null是安全的。

   class Foo {
        private static MyObject obj;
        public static MyObject GetMyObject() {
            MyObject o = obj;
            if(o == null) {
                o = new MyObject();
                obj = o;
            }
            return o;
        }
        public static void ClearMyObject() {
            obj = null;
        }
    }

    class MyObject {}

4 个答案:

答案 0 :(得分:6)

  

这个线程安全吗?

没有

  

GetMyObject()方法是否可以返回null?

没有。

保证该方法永远不会返回null。并且所有的读写都保证是原子的。但是,线程无法保证读取最新版本的静态字段obj ,并且线程无法保证对obj 的更改顺序具有一致的视图。任意多个线程可能会竞争并观察 obj 的不同值。我不认为这个代码“线程安全”,但也许你有一个不同的“线程安全”定义。这是问这个问题的问题;没有标准的术语定义,每个人都可以认可。

答案 1 :(得分:5)

GetMyObject()永远不会返回null。看到这个的简单方法是注意'o'是一个局部变量,所以没有其他人可以影响它。

答案 2 :(得分:4)

好吧,让我们通过这个理由:

public static MyObject GetMyObject() {
        MyObject o = obj;
        if(o == null) {
            o = new MyObject();
            obj = o;
        }
        return o;

}

只有一个return声明。此方法可以产生null返回值的唯一方法是,如果唯一的return语句return o在执行时o == nulltrue

如果执行onullreturn o,则表示我们if阻止了o null oo null o == null true if o == null false o != null o o == null块已经过测试(如果trueif,则o = new MyObject()为真,因为o是局部变量,所以它不会受到任何其他线程的影响。但是null 1}}为if意味着我们最终进入obj = o块内部,现在当构造函数调用o返回时,我们保证o不是ooo == null中的第二个语句不会影响true的值。再次,由于false是局部变量,因此无关紧要通过此代码路径有多个线程:每个线程都有自己的o == null,没有其他线程可以触及任何其他线程的false

因此,无论ifMyObject还是Lazy<T>,当private static Lazy<MyObject> obj; static Foo() { obj = new Lazy<MyObject>( () => new MyObject(), true ); } public static MyObject GetMyObject() { return obj.Value; } public static void ClearMyObject() { obj = new Lazy<MyObject>( () => new MyObject(), true ); } 块完成时,我们最终会{{1}}为{{1}}

因此,保证此方法返回非空值。

  

我只想确保假设GetMyObject()永远不会返回null是安全的。

嗯,如果那就是你关心的那就没关系。但是让我们清楚一些事情。您的方法不是线程安全的。完全有可能构造两个{{1}}实例,并且两个不同的调用者最终可能会看到不同的返回值,即使很明显您的意图只有一个。要解决此问题,我建议您使用{{1}}:

{{1}}

答案 3 :(得分:1)

它不会返回null,但它绝不是大多数公认的定义的线程安全。据推测,您希望将对象存储到共享状态,并让其他线程访问该对象。在这种情况下,其他线程可以创建自己的副本(如您所说)并尝试存储它们,但不保证所有线程都能看到该对象的最新版本(或该对象的任何其他线程版本)。同样,您的ClearMyObject()方法也不会按照您的想法执行。

使用Lazy<T>代替您提供所需的内容。

public static readonly Lazy<MyObject> myObject = new Lazy<MyObject>(() => new MyObject(), true);