对象赋值线程安全吗?
本文关键字:安全 线程 赋值 对象 | 更新日期: 2023-09-27 18:10:31
这个线程安全吗?具体来说,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 {}
号这个线程安全吗?
是否有可能为GetMyObject()方法返回null?
。
方法保证永远不会返回null。所有的读和写都保证是原子的。但是,线程不能保证读取静态字段obj的最新版本,并且线程不能保证对obj的更改顺序具有一致的视图。任意多个线程可能会竞争并观察到obj的不同值。我不认为这段代码是"线程安全的",但也许你对"线程安全"有不同的定义。这就是问这个问题的问题;
GetMyObject()永远不会返回null。最简单的方法是注意到'o'是一个局部变量,因此没有其他人可以影响它。
好吧,让我们来推理一下:
public static MyObject GetMyObject() {
MyObject o = obj;
if(o == null) {
o = new MyObject();
obj = o;
}
return o;
}
只有一个return
语句。该方法产生null
返回值的唯一方式是,如果唯一的return
语句return o
在执行时o == null
为true
。
如果return o
执行时o
是null
,这意味着我们从if
块中取出o
作为null
。我们可以从o
块中取出o
作为null
的唯一方法是,当if
块的条件被测试时,如果o == null
是true
(如果o == null
是false
,那么o != null
是true,并且由于o
是一个局部变量,它不会受到任何其他线程的影响。但是o == null
是true
意味着我们最终进入了if
块,现在当构造函数调用o = new MyObject()
返回时,我们可以保证o
不是null
。if
块中的第二个语句obj = o
不会影响o
的值。同样,由于o
是一个局部变量,如果有多个线程在此代码路径中燃烧,则无关重要:每个线程都有自己的o
,并且没有其他线程可以触摸任何其他线程的o
。
因此,无论o == null
是true
还是false
,当if
块完成时,我们最终得到o == null
是false
。
因此,该方法保证返回一个非空值。
我只是想确保假设GetMyObject()永远不会返回null是安全的。
好吧,如果你只关心这个,那也没关系。但是让我们弄清楚一些事情。你的方法不是线程安全的。完全有可能构造两个MyObject
实例,并且两个不同的调用者最终可能看到不同的返回值,即使您的意图很明显只有一个。为了解决这个问题,我建议使用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
);
}
它不会返回null,但根据大多数公认的定义,它绝不是线程安全的。假设您希望将对象存储为共享状态,并让其他线程访问该状态。在这种情况下,其他线程可能会创建自己的副本(如您所说)并尝试存储它们,但不能保证所有线程都能看到该对象的最新版本(或该对象的任何其他线程版本)。同样,你的ClearMyObject()
方法也不会像你想象的那样。
使用Lazy<T>
代替,它将提供你正在寻找的。
public static readonly Lazy<MyObject> myObject = new Lazy<MyObject>(() => new MyObject(), true);