2016-03-03 65 views
22

提前道歉:这个问题来自于一个努力学习高级C#的硬核,未改编的C++开发人员。考虑以下内容:是C#6吗? (猫王op)线程安全吗?如果是这样,怎么样?

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

这显然不是线程安全的。另一方面,我见过两篇教程说? (消零条件运算符或 '猫王运营商')例如,

myUserDefinedObject?.ToString(); 

IS线程安全的。除非编译器在封面(shiver)下包裹一个[mutex?]锁定,否则我不明白这是怎么回事。如果这个习语是线程安全的,那么有人能够指出我是如何完成该技术的技术描述的?如果它不是线程安全的,有没有人有一个实际上说它不是?

+3

对不起,但您的第一个代码块可以完全线程安全,具体取决于使用它的上下文和变量的范围。 –

+2

@KenWhite - 我认为第一个块的想法是,另一个线程可能会在检查之后但在.ToString()导致代码失败之前将该变量设置为null。我会说它不是线程安全的。 – Enigmativity

+0

@Enigmativity:海报明确表示,该代码块是**不是线程安全**,如果不知道上下文或范围,这不是一个准确的陈述。我指出,该声明是不准确的 - 这是不真实的,该块中的代码明确不是线程安全的。 –

回答

24

MSDN(重点煤矿):

为空状态成员访问的另一个用途是用更少的代码调用代表以线程安全的方式。旧的方式需要像下面的代码:

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

的新方法则简单得多:

PropertyChanged?.Invoke(e) 

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

所以没有这里涉及到锁定 - 线程安全性是通过创建一个本地临时变量,以防止不同的线程修改变量空校验和其他一些操作之间执行。

+6

因此,它是如何线程安全的取决于你想要做什么。如果另一个线程调用Dispose(),您可能会得到一个ObjectDisposedException,但您不会在调用站点获得空指针异常。 –

+0

我会更倾向于相信MSDN文档,如果有人能够解释在C#中,如何在内存地址检查为非零时,以及在尝试访问内容时未知的某些未知时间该地址的内容未被修改。从我上面引用的文章中,我们知道可以使用* hardware *来标记该位置(使用atomics,memory_modes等)。那是“编译器”在做什么? @Hank,你也可能会得到一个答案 - 而不是你期待的对象仍然在那个位置。 ;-) –

+3

这是不是真的线程安全,因为[说明](https://msdn.microsoft.com/en-us/magazine/jj883956.aspx)请参阅“阅读简介”部分。 JIT有时可以完全消除局部变量并将另一个读取引入实际字段。 –

28

我想澄清BJ Myers的(正确)答案。

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

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

不是线程安全的。该值可能会发生变化,以便在检查之前为非空,在检查之后为空,并且程序崩溃。

使这个“线程安全”,我使用术语通常,通常的方法是将值复制到本地,然后测试本地为空。这具有不会导致null解除引用的好处。但是,聪明的开发者会注意到还有一场比赛!该序列可以缓存在线程

  • 非空的事件处理程序
  • 事件处理程序设置为null的线程B
  • 的事件处理程序需要被销毁线程B国
  • 事件处理程序运行在线程A并死亡可怕

因此,在这个意义上这种模式不是“线程安全”的。如果您处于这个不幸的位置您有责任确保实施适当的线程逻辑,以便不会发生这种情况。无论你想要什么,你都可以做到。如果你希望能够在一个线程上调用事件处理程序(在另一个线程上改变事件)时获得(可疑的)好处,那么你必须付出代价才能使其安全或处理竞态条件错误。

我个人会避免像鼠疫这种情况,但我不够聪明,写出正确的多线程代码。

现在,作为实际的问题:

some_expression ?. ToString(); 

相同

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

是在您看来,后者的代码 “线程”?

+0

伟大的评论在这里 - 感谢您的优秀解释。 “线程安全性”完全取决于线程使用代码的方式。 –

+0

那么......不安全吗?还是它?我们是否需要使用一个锁来避免崩溃,如果我们有多个线程在乱搞? – Narvalex

+4

@Narvalex:我应该怎么知道?解释*你在哪里放置锁*,以及*锁可以防止什么竞争*,以及*你的策略是为了避免死锁*,然后我们可以在决定你的解决方案是否正确时做出一些决定。请记住,*线程安全性是整个程序的属性*,因此如果您想知道某个程序是否是线程安全的,则必须提供整个程序的详细信息。 *这就是为什么线程很难*。你拿着一块砖,问“这块砖是用结构上的声音做成的房子吗?”这是房子的财产,而不是砖头。 –

相关问题