为空COALESCE运营线程安全的? [英] Is the null coalesce operator thread safe?
问题描述
因此,这是问题的肉:可FOO.BAR以往任何时候都返回null?为了澄清,可以'_bar'被设置为空后,它评为非空之前它的返回值?
公共类Foo
{
对象_bar;
公共对象吧
{
{返回_bar?新的对象(); }
集合{_bar =价值; }
}
}
我知道用下面的get方法并不安全,并且可以返回空值:
{返回_bar!= NULL? _bar:新的对象(); }
更新:
另一种方式来看待同一个问题,这个例子可能更清楚:
公共静态牛逼的GetValue< T>(REF的T值),其中T:类,新的()
{
返回值?新T();
}
和再次询问能的GetValue(...)永远返回null?根据你的定义,这可能是也可能不是线程安全的......我猜对问题的陈述是问,如果它是价值一个原子操作...大卫偏航已经说定义的问题最好是上述功能等价以下内容:
公共静态牛逼的GetValue< T>(REF的T值),其中T:类,新的()
{
牛逼的结果=值;
如果(结果!= NULL)
返回结果;
其他
返回新T();
}
不,这不是线程安全的。
的IL为上述编译为:
。方法公开hidebysig specialname对象实例get_Bar()CIL管理
{
.maxstack 2
.locals的init(
[0]对象CS $ 1 $ 0000)
L_0000:NOP
L_0001:ldarg.0
L_0002:ldfld对象ConsoleApplication1.Program / MainClass :: _吧
L_0007:DUP
L_0008:brtrue.s L_0010
L_000a:流行
L_000b:newobj实例无效[mscorlib程序] System.Object的构造函数::()
L_0010:stloc.0
L_0011:br.s L_0013
L_0013:ldloc.0
L_0014:RET
}
这有效地做了 _bar
字段的负载,然后检查它的存在,并跳转OT结束。有没有适当的同步,而由于这是多个IL指令,有可能为辅助线程导致竞争条件 - 导致返回的对象从一组不同
这是更好的通过 延迟&LT处理延迟实例; T>
。它提供了一个线程安全的,懒惰的实例化模式。当然,上面的code是没有做懒惰的实例化(而返回一个新的对象的每次的,直到当 _bar
设置一段时间),但我怀疑这是一个错误,而不是预期的行为。
此外,延迟< T>
使守业难
要复制在一个线程安全的方式对上述行为将需要显式同步。
对于您的更新:
吸气的酒吧财产永远不会返回null。
综观IL以上,它 _bar
(通过ldfld),然后做了检查,看看是否能使用的对象不为空<一个href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.op$c$cs.brtrue_s.aspx">brtrue.s.如果对象不为空,它跳跃,通过<一份 _bar值
从执行堆栈到本地href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.op$c$cs.stloc_0.aspx">stloc.0,并返回 - 返回 _bar
用实际值
如果 _bar
被清除的,那么它会弹出它关闭执行堆栈,并创建一个新的对象,然后获取存储和返回。
无论哪种情况下,prevents被返回的空
值。但是,同样,我也不会考虑这个线程安全的一般,因为它可能是一个调用设置在同一时间,一个电话来获得可引起不同的对象发生退货,这是一个竞争条件的哪个对象实例得到返回(设定值时,或者一个新的对象)。
So this is the meat of the question: Can Foo.Bar ever return null? To clarify, can '_bar' be set to null after it's evaluated as non-null and before it's value is returned?
public class Foo
{
Object _bar;
public Object Bar
{
get { return _bar ?? new Object(); }
set { _bar = value; }
}
}
I know using the following get method is not safe, and can return a null value:
get { return _bar != null ? _bar : new Object(); }
UPDATE:
Another way to look at the same problem, this example might be more clear:
public static T GetValue<T>(ref T value) where T : class, new()
{
return value ?? new T();
}
And again asking can GetValue(...) ever return null? Depending on your definition this may or may not be thread-safe... I guess the right problem statement is asking if it is an atomic operation on value... David Yaw has defined the question best by saying is the above function the equivalent to the following:
public static T GetValue<T>(ref T value) where T : class, new()
{
T result = value;
if (result != null)
return result;
else
return new T();
}
No, this is not thread safe.
The IL for the above compiles to:
.method public hidebysig specialname instance object get_Bar() cil managed
{
.maxstack 2
.locals init (
[0] object CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: ldfld object ConsoleApplication1.Program/MainClass::_bar
L_0007: dup
L_0008: brtrue.s L_0010
L_000a: pop
L_000b: newobj instance void [mscorlib]System.Object::.ctor()
L_0010: stloc.0
L_0011: br.s L_0013
L_0013: ldloc.0
L_0014: ret
}
This effectively does a load of the _bar
field, then checks its existence, and jumps ot the end. There is no synchronization in place, and since this is multiple IL instructions, it's possible for a secondary thread to cause a race condition - causing the returned object to differ from the one set.
It's much better to handle lazy instantiation via Lazy<T>
. That provides a thread-safe, lazy instantiation pattern. Granted, the above code is not doing lazy instantiation (rather returning a new object every time until some time when _bar
is set), but I suspect that's a bug, and not the intended behavior.
In addition, Lazy<T>
makes setting difficult.
To duplicate the above behavior in a thread-safe manner would require explicit synchronization.
As to your update:
The getter for the Bar property could never return null.
Looking at the IL above, it _bar
(via ldfld), then does a check to see if that object is not null using brtrue.s. If the object is not null, it jumps, copies the value of _bar
from the execution stack to a local via stloc.0, and returns - returning _bar
with a real value.
If _bar
was unset, then it will pop it off the execution stack, and create a new object, which then gets stored and returned.
Either case prevents a null
value from being returned. However, again, I wouldn't consider this thread-safe in general, since it's possible that a call to set happening at the same time as a call to get can cause different objects to be returned, and it's a race condition as which object instance gets returned (the set value, or a new object).
这篇关于为空COALESCE运营线程安全的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!