C#凝聚算子的原子性
本文关键字:原子性 凝聚 | 更新日期: 2023-09-27 18:25:32
我今天在我们的代码库中遇到了一些单例代码,我不确定以下代码是否是线程安全的:
public static IContentStructure Sentence{
get {
return _sentence ?? (_sentence = new Sentence());
}
}
此语句相当于:
if (_sentence != null) {
return _sentence;
}
else {
return (_sentence = new Sentence());
}
我相信??只是编译器的一个技巧,并且生成的代码仍然不是原子代码。换句话说,在将_sentence设置为新句子并返回之前,两个或多个线程可能会发现_sentence为null
为了保证原子性,我们必须锁定这段代码:
public static IContentStructure Sentence{
get {
lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); }
}
}
这都对吗?
我今天在我们的代码库中遇到了一些单例代码
你的代码库中有这样的模糊代码吗?这个代码做同样的事情:
if (_s == null)
_s = new S();
return _s;
阅读起来容易一千倍。
我相信??只是编译器的一个技巧,并且生成的代码仍然不是原子
你是对的。C#对原子性做了以下保证:
以下数据类型的读取和写入是原子类型:bool、char、byte、sbyte、short、ushort、uint、int、float和引用类型。此外,对前面列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型的读取和写入,包括long、ulong、double和decimal,以及用户定义的类型,不能保证是原子类型。除了为此目的设计的库函数之外,不能保证原子读-修改-写,例如在递增或递减的情况下。
null合并运算符不在该保证列表中。
为了保证原子性,我们必须锁定这段代码:
lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); } } }
天哪,不。它马上就崩溃了!
正确的做法是:
- 停止尝试编写多线程代码
- 使用Jon Skeet在其关于singleton的页面上文档中的一个安全singleton模式编写singleton
- 使用
Lazy<T>
类 - 锁定专用于锁定该变量的对象
- 使用Interlocked Compare Exchange进行原子测试和设置
您是正确的;它根本不安全。
您可以将Interlocked.CompreExchange与null
一起使用,以获得原子的??
式操作。
// I made up my own Sentence type
Sentence current = null;
var whenNull = new Sentence() {Text = "Hello World!"};
var original = Interlocked.CompareExchange(ref current, new Sentence() { Text = "Hello World!" }, null);
Assert.AreEqual(whenNull.Text, current.Text);
Assert.IsNull(orig);
// try that it won't override when not null
current.Text += "!";
orig = Interlocked.CompareExchange(ref current, new Sentence() { Text = "Hello World!" }, null);
Assert.AreEqual("Hello World!!", current.Text);
Assert.IsNotNull(orig);