在C#中,`??`是线程安全的吗?

3
简单问题: "??"、"?."和"? :"是否线程安全?我能够信赖它们吗?或者我应该使用其它线程安全的解决方案? 例如下面的代码:
public static T Instance => _Instance ?? (_Instance = CreateInstance());

线程安全吗?


2
你在这里的问题不应该是 ?? 是否线程安全,而应该是 CreateInstance() 是否安全。 - InBetween
1
如果你假设CreateInstance()是线程安全的,即使如此,你仍然需要问一下??表达式是否也是线程安全的。 - Matthew Watson
@InBetween 假设 CreateInstance() 是线程安全的。我想关注 ?? - Mohammad Mirmostafa
@MatthewWatson 是的,CreateInstance() 是线程安全的。那么 ?? 呢? - Mohammad Mirmostafa
“?. ”是一种非常不同的情况,但不适用于您的示例。 - H H
2个回答

2
?? 在C#中读取的是保证原子性的引用(写入也是如此)。因此,如果您的问题是在C#中是否可以有破碎的引用读取或写入,则答案是否定的。
现在,public static T Instance => _Instance ?? (_Instance = CreateInstance()); 线程安全吗?一般的答案是否定的,因为该表达式有一次读取和一次写入,这绝对不是一个原子操作。
可能出现的问题:
1. _Instance 的竞争条件读取:一个线程可能会在第二个线程初始化它之前将其读取为 null。结果是运行了两次 CreateInstance()。这是一个问题吗?如果是,那么它就不是线程安全的。如果不是,那么它可能是线程安全的,但从性能的角度来看并不理想,但可能是安全的。
2. CreateInstance() 的并发执行怎么样?由于前面的竞争条件,也有可能一个线程在另一个线程退出之前进入 CreateInstance。现在你的问题不仅是 CreateInstance 运行两次,而且两者(或更多)同时运行。这是安全的吗?
因此,一般来说,这与 CreateInstance() 的安全性一样安全。

1
同意。它不是线程安全的,特别是如果您不想两次调用CreateInstance(),那么这种竞争条件就会变得非常棘手。 - Matthew Watson

2
它不是线程安全的,因为存在竞争条件。(例如,线程A检查值,发现它为空,线程B检查值,发现它为空,线程B使用CreateInstance()调用初始化值,线程A使用CreateInstance()调用初始化值。)
线程安全地初始化单例的正确方法是使用Lazy<T>类,例如:
public T Instance => _instance.Value;

static Lazy<T> _instance = new Lazy<T>(CreateInstance);

CreateInstance() 返回一个类型为 T 的实例。


我应该在CreateInstance()中使用lock吗? - Mohammad Mirmostafa
@MohammadMirMostafa,这并没有帮助,因为您需要同步访问条件(??)和赋值(CreateInstance())。相反,最好使用Lazy<>,它会为您正确执行所有同步操作。参考链接 - Sinatr
如果它可以从多个线程中调用,并且这样做可能会引起问题,那么是的——但是在没有看到其实现的情况下,我无法确定。请注意,如果它只被从“Lazy”对象中调用,则绝对不需要锁定它。 - Matthew Watson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接