对象分配线程安全吗?
这个线程安全吗?具体来说,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 {}
Is this thread safe? Specifically, is it possible for the GetMyObject()
method to return null? I understand it is possible for two threads to get a different instance of MyObject
but I don't care about that. I just want to make sure it is safe to assume GetMyObject()
will never return 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 {}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不。
不会。
该方法保证永远不会返回 null。并且所有的读取和写入都保证是原子的。但是,不能保证线程读取静态字段 obj 的最新版本,并且不能保证线程对 obj 的更改顺序具有一致的视图。任意多个线程可能会竞争并观察到 obj 的不同值。我不会认为这段代码是“线程安全”的,但也许你对“线程安全”有不同的定义。这就是问这个问题的问题所在;对于这个术语,没有一个大家都认可的标准定义。
No.
No.
The method is guaranteed to never return null. And all the reads and writes are guaranteed to be atomic. However, threads are not guaranteed to read the latest version of static field obj, and threads are not guaranteed to have a consistent view of the sequence of changes to obj. Arbitrarily many threads may race and observe different values of obj. I would not consider this code "thread safe" as a result, but maybe you have a different definition of "thread safe". That's the problem with asking this question; there's no standard definition of the term that everyone reliably agrees upon.
GetMyObject() 永远不能返回 null。看到这一点的简单方法是注意“o”是一个局部变量,因此没有其他人可以影响它。
GetMyObject() can never return null. The easy way to see this is to note that 'o' is a local variable so no-one else can affect it.
好吧,让我们来推理一下:
}
只有一个
return
语句。此方法可以生成null
返回值的唯一方法是,如果唯一的return
语句return o
具有o == null< /code> 执行时为
true
。如果执行
return o
时o
为null
,则意味着我们使用从
为if
块中出来>onull
。我们可以从o
块中将o
作为null
出来的唯一方法是,如果o == null
是true
当测试if
块的条件时(如果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
,我们最终得到o == null
当if
块完成时,false
。因此,该方法保证返回非空值。
好吧,如果这就是您关心的一切,那很好。但让我们澄清一件事。您的方法不是线程安全的。完全有可能构造两个
MyObject
实例,并且两个不同的调用者最终可能会看到不同的返回值,即使很明显您的意图是只拥有一个。要解决此问题,我建议仅使用Lazy
:Well, let's reason through this:
}
There is only one
return
statement. The only way that this method can produce anull
return value is if that solereturn
statementreturn o
haso == null
beingtrue
when it is executed.If
o
isnull
whenreturn o
is executed, that means we came out of theif
block witho
asnull
. The only way we can come out of theo
block witho
asnull
is ifo == null
wastrue
when the conditional for theif
block was tested (ifo == null
isfalse
, theno != null
is true and sinceo
is a local variable it can not be affected by any other thread. But theno == null
beingtrue
implies we end up inside theif
block and now when the constructor invocationo = new MyObject()
returns, we are guaranteed thato
is notnull
. The second statement in theif
block,obj = o
, does not affect the value ofo
. Again, sinceo
is a local variable, it doesn't matter if there are multiple threads burning through this code path: each thread has its owno
and no other thread can touch any other thread'so
.Therefore, whether or not
o == null
istrue
orfalse
, we end up witho == null
beingfalse
when theif
block completes.Therefore, this method is guaranteed to return a non-null value.
Well, that's fine if that's all you care about. But let's be clear about something. Your method is NOT thread safe. It is perfectly possible for two instances of
MyObject
to be constructed and two different callers could end up seeing different return values even though it is clear that you intention is to only have one. To fix this, I recommend just usingLazy<T>
:它不会返回 null,但根据大多数接受的定义,它绝不是线程安全的。据推测,您希望将对象存储为共享状态并让其他线程访问该对象。在这种情况下,其他线程可能会创建自己的副本(如您所说)并尝试存储它们,但不能保证所有线程都能看到该对象的最新版本(或该对象的任何其他线程版本)。同样,您的
ClearMyObject()
方法也不会执行您认为会执行的操作。使用
Lazy
代替,它将提供您正在寻找的内容。public static readonly Lazy; myObject = new Lazy(() => new MyObject(), true);
It won't return null, but it is by no means thread-safe by most accepted definitions. Presumably, you want to store your object into shared state and have that accessed by other threads. In this case, other threads may create their own copies (as you said) and try to store them, but all threads are not guaranteed to see the latest version of that object (or any other thread's version of that object). Likewise, your
ClearMyObject()
method will not do what you think it will.Use
Lazy<T>
instead which will provide what you're looking for.public static readonly Lazy<MyObject> myObject = new Lazy<MyObject>(() => new MyObject(), true);