是C#6? (Elvis op)线程安全吗?如果是这样,怎么样?

时间:2016-03-03 23:07:22

标签: c# c#-6.0 null-conditional-operator

提前道歉:这个问题来自一个试图学习高级C#的核心,未经过改造的C ++开发人员。请考虑以下事项:

if (myUserDefinedObject != null)
{
    myUserDefinedObject.ToString();
}

这显然不是线程安全的。另一方面,我看过两个教程说? (例如,Null条件运算符或'Elvis运算符')

myUserDefinedObject?.ToString();

IS 线程安全。除非编译器将[mutex?]锁定在它下面(颤抖),否则我不明白这是怎么回事。如果这个成语是线程安全的,有人可以指点我如何完成它的技术描述?如果它不是线程安全的,那么有没有人有一个实际上说它不是的参考?

2 个答案:

答案 0 :(得分:31)

我想澄清BJ迈尔斯的(正确)答案。

在C#中,事件可以被认为是委托类型的字段 - 正如属性可以被认为是属性类型的字段 - 并且该“字段”的值可以为空。如果你处于一个线程试图调用它的同时在一个线程上修改事件处理程序的不幸情况,你可以进入以下情况:

if (this.SomeEvent != null) 
    this.SomeEvent( ... );

不是线程安全的。该值可能会被突变,因此在检查之前它是非null,在检查之后为null,并且程序崩溃。

使这个“线程安全”的常用方法,我使用的术语是将值复制到本地,然后测试本地为null。这样做的好处是不会因空取消引用而崩溃。然而,聪明的开发人员会注意到还有一场比赛!序列可以是

  • 在线程A上缓存的非null事件处理程序
  • 在线程B上将事件处理程序设置为null
  • 事件处理程序所需的状态在线程B上被销毁
  • 事件处理程序在线程A上运行并且可怕地死亡

因此,在这种意义上,这种模式不是“线程安全的”。如果您处于这个不幸的位置,您有责任确保实施适当的线程逻辑,以便不会发生这种情况。你可以随心所欲地做到这一点。如果你想要在一个线程上调用一个事件处理程序同时在另一个线程上改变事件的(有问题的)好处,那么你必须付钱以使其安全,或者处理竞争条件错误。

我个人会像瘟疫那样避免这种情况,但我不够聪明,无法编写正确的多线程代码。

现在,关于实际问题:

some_expression ?. ToString();

相同
temp = some_expression
temp == null ? null : temp.ToString()

您认为后一个代码是“线程安全”吗?

答案 1 :(得分:26)

来自MSDN(强调我的):

  

null条件成员访问的另一个用途是以线程安全的方式使用更少的代码调用委托。旧方法需要如下代码:

var handler = this.PropertyChanged;
if (handler != null)
    handler(…)
     

新方法更简单:

PropertyChanged?.Invoke(e)
     

新方法是线程安全的,因为编译器只生成一次评估PropertyChanged的代码,将结果保存为临时变量

因此这里不涉及锁定 - 通过创建本地临时变量来强制执行线程安全性,这可以防止不同的线程在null检查和其他操作之间修改该变量。