null coalesce操作符线程是否安全?

csh*_*net 24 .net c# coalesce thread-safety

所以这就是问题的关键:Foo.Bar可以返回null吗?为了澄清,'_bar'在被评估为非null并且在返回值之前可以设置为null吗?

    public class Foo
    {
        Object _bar;
        public Object Bar
        {
            get { return _bar ?? new Object(); }
            set { _bar = value; }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我知道使用以下get方法不安全,并且可以返回null值:

            get { return _bar != null ? _bar : new Object(); }
Run Code Online (Sandbox Code Playgroud)

更新:

另一种看待同一问题的方法,这个例子可能更清楚:

        public static T GetValue<T>(ref T value) where T : class, new()
        {
            return value ?? new T();
        }
Run Code Online (Sandbox Code Playgroud)

并再次询问GetValue(...)是否会返回null?根据你的定义,这可能是也可能不是线程安全的...我猜正确的问题陈述是询问它是否是一个关于价值的原子操作...... David Yaw已经通过说上面的函数等效来定义问题了以下内容:

        public static T GetValue<T>(ref T value) where T : class, new()
        {
            T result = value;
            if (result != null)
                return result;
            else
                return new T();
        }
Run Code Online (Sandbox Code Playgroud)

Ree*_*sey 22

不,这不是线程安全的.

以上的IL编译为:

.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 
}
Run Code Online (Sandbox Code Playgroud)

这有效地完成了_bar场的加载,然后检查它的存在,并跳过结束.没有同步,并且因为这是多个IL指令,所以辅助线程可能导致竞争条件 - 导致返回的对象与一组不同.

处理延迟实例化会更好Lazy<T>.这提供了一个线程安全,懒惰的实例化模式.当然,上面的代码没有进行延迟实例化(而是每次都返回一个新对象,直到_bar设置了一段时间),但我怀疑这是一个错误,而不是预期的行为.

另外,Lazy<T>使设置变得困难.

要以线程安全的方式复制上述行为,需要显式同步.


至于你的更新:

Bar属性的getter永远不会返回null.

查看上面的IL,它_bar(通过ldfld)然后使用brtrue.s检查该对象是否为空.如果对象不为null,则跳转,_bar将执行堆栈的值从stloc.0复制到本地,然后返回 - 返回_bar实际值.

如果_bar未设置,则会将其从执行堆栈中弹出,然后创建一个新对象,然后存储并返回该对象.

这两种情况都会阻止null返回值.但是,我一般不会认为这个线程安全,因为调用set的同时调用get可能会导致返回不同的对象,并且它是一个竞争条件,因为它是对象返回实例(设置值或新对象).