未使用的撕裂读取未定义的行为?

Cra*_*igM 7 c# multithreading undefined-behavior memory-barriers

题:

有些模式(例如这里的C#/ CLR:MemoryBarrier和撕裂的读取)可以执行撕裂的读取,但如果可能发生了撕裂的读取,则永远不会使用结果值.

这是C#中未定义的行为吗?

相关:我如何确定这是否是未定义的行为?

尝试确定答案失败:

我的理解(纠正我,如果我错了)是在C++ 11中这将是未定义的行为,因为它没有被在C++ 11中添加的内存模型定义(在旧版本中所有多线程都是特定于实现的行为).从理论上讲,我可以从规范中确定这一点.

我试图为C#做这个并且失败了.我在规范中找不到内存模型.以下是我通过C#语言规范(http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf)尝试追踪分配一个结构的语义的不成功之旅的一些注释.在可能存在数据竞争的情况下(一个读取器复制,一个写入器写入正在复制的数据)的另一个.在任何场景中,我都找不到分配或读取任何变量的语义.要么我错过了某些东西(几乎可以肯定是这种情况),要么使用变量是未定义的行为.

阅读C#规范相关部分时的笔记:

C#语言规范说明什么类型是原子的,但从来没有它用于原子和非原子读写的测量:

12.5变量引用的原子性

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

它没有说"原子"(它没有定义)这里是否暗示任何围栏(所以我假设没有),但是对于这个问题更相关,它甚至没有定义读写的内容(这是非平凡的)多线程程序).

14.14.1 Simple assignment,这似乎是写规范的范围(解释"x = y"):

由y的评估和转换产生的值存储在由x的评估给出的位置中.

8.3 Variables and parameters 状态:

在获得其值之前,应分配变量.

但没有定义获取值.我在哪里看到读取产生的规范(人们会假设你在单线程情况下写的最后一件事,所以我在规范中找不到).

10.10 Execution order 似乎不必要地约束(与下面链接的MSDN文章中使用的正常获取和释放语义相比)易于写入(没有写入可以跨越对volatile的引用向任一方向移动)而在约束读取时(它们可以同时移动)这也没有提到Thread.MemoryBarrier(谁的文档似乎阻止了处理器而不是编译器重新排序所以它太弱了.)它也没有提到从变量/字段加载的意思,所以很多讨厌的offtopic问题,但没有答案.

我已经阅读了我能找到的相关规范的所有部分.没有任何地方从字段/内存/变量(看起来像"变量"这里是正确的术语)中读取定义.

也许在语言规范的某个地方有一个读取/加载和写入/存储变量行为的规范(又名:内存模型),但如果有(我找不到它),它就不会引用"原子" "(我搜索它的原子:第12.5节是唯一用途).我没有看到可能如何定义任何C#代码,所以很明显我遗漏了一些东西:我认为有效的C#实现不会在任何读取或写入时退出(由于它是未定义的行为!)值.

如果多线程(甚至可能是单线程)C#实际上在没有内存模型的情况下严重不足,那么有没有可以查看特定实现的规范?例如:如果C#没有定义读写的语义,也许微软的各种C#编译器(现在至少有3个)提供规范,Mono也是如此?这在任何实现中都是安全的吗?最好的方法是什么?

也许有一些非正式(不符合规范)规则被认为是安全的(所有主要实现都遵守)?这将是可怕的,但如果这就是我们得到的,我会接受它.

一些相关但不充分的来源:

我发现这篇文章声称是关于C#内存模型,但它是特定于实现的(参考CLR),并未涵盖相关案例.它还提供了一个很好的清晰解释,我喜欢它的易失性语义(C++ 11风格获得一个版本),但在某些方面更强,而且除了10.10 Execution order语言规范之外更弱,所以我认为它错了:https://msdn.microsoft.com/magazine/jj863136

这篇文章似乎有很多比较C#内存模型和C++ 11的好信息,但据我所知,它还没有涵盖这个特定的问题:http://blog.alexrp.com/2014/03/30 /点净原子能公司和记忆模型,语义/

一篇关于重新排序问题的好文章.最后的更新提到"易失性读取可以在时间上相对于易失性写入向后移动",这与10.10 Execution order规范不一致,但同意大多数人认为声称语义应该是什么(意味着它是一致的)使用MSDN文章和Volatile类,但据我所知,不是关键字):http://blog.coverity.com/2014/03/12/can-skip-lock-reading-integer/#.VoSyfhXhDGg

从快速浏览看,本文主要介绍volatile以及它如何防止冗余负载消除以及为什么需要:https://blogs.msdn.microsoft.com/ericlippert/2011/06/16/atomicity-volatility-and-immutability-are -不同部分三/