C#聚结算子的原子性

Ada*_*dam 7 c# atomic null-coalescing-operator

我今天在代码库中遇到了一些单例代码,我不确定以下是否是线程安全的:

public static IContentStructure Sentence{ 
    get {
       return _sentence ?? (_sentence = new Sentence()); 
    }
}
Run Code Online (Sandbox Code Playgroud)

该陈述相当于:

if (_sentence != null) {
       return _sentence;
}
else {
    return (_sentence = new Sentence());
}
Run Code Online (Sandbox Code Playgroud)

我相信 ??只是一个编译器技巧,结果代码仍然不是原子的.换句话说,在将_sentence设置为新句子并返回之前,两个或多个线程可以找到_sentence为null.

为了保证原子性,我们必须锁定那段代码:

public static IContentStructure Sentence{ 
    get {

       lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); }
    }
}
Run Code Online (Sandbox Code Playgroud)

那一切都正确吗?

Eri*_*ert 16

我今天在代码库中遇到了一些单例代码

你的代码库中是否有这种混淆代码?这段代码做了同样的事情:

if (_s == null) 
    _s = new S();
return _s;
Run Code Online (Sandbox Code Playgroud)

并且大约容易阅读一千倍.

我相信 ??只是一个编译器技巧,结果代码仍然不是原子的

你是对的.C#提供以下原子性保证:

以下数据类型的读取和写入是原子的:bool,char,byte,sbyte,short,ushort,uint,int,float和reference类型.此外,在先前列表中具有基础类型的枚举类型的读取和写入也是原子的.其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的.除了为此目的而设计的库函数之外,不保证原子读 - 修改 - 写,例如在递增或递减的情况下.

空合并运算符不在该保证列表上.

为了保证原子性,我们必须锁定那段代码:

lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); } } }    
Run Code Online (Sandbox Code Playgroud)

天哪没有.那立刻就崩溃了!

正确的做法是:

  • 停止尝试编写多线程代码.
  • 在他的页面上使用一个安全的单例模式Jon Skeet文档写一个关于单身人士的单身人士.
  • 使用该Lazy<T>课程.
  • 锁定专用于锁定该变量的对象.
  • 使用Interlocked Compare Exchange进行原子测试和设置.


SLa*_*aks 12

你是对的; 它根本不是线程安全的.