为空COALESCE运营线程安全的? [英] Is the null coalesce operator thread safe?

查看:102
本文介绍了为空COALESCE运营线程安全的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此​​,这是问题的肉:可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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆