我的问题是关于C#中执行保证的顺序(大概是.Net)。我给出了一些我知道要比较的Java示例。
For Java(来自“Java Concurrency in Practice”)
无法保证一个线程中的操作将按程序给出的顺序执行,只要在该线程内无法检测到重新排序 - 即使重新排序对其他线程很明显。
所以代码
y = 10;
x = 5;
a = b + 10;
实际上可以指定a = b + 10在指定y = 10
之前在Java中(来自同一本书)
当线程A启动由同一个锁保护的同步块时,线程A在同步块中或之前执行的所有操作都可见。
所以在Java中
y = 10;
synchronized(lockObject) {
x = 5;
}
a = b + 10;
y = 10且x = 5保证在a = b + 10之前运行(我不知道y = 10是否保证在x = 5之前运行)。
C#代码对C#语句的执行顺序有什么保证
y = 10;
lock(lockObject) {
x = 5;
}
a = b + 10;
我特别感兴趣的答案可以提供明确的参考或其他一些非常有意义的理由,因为这样的保证很难测试,因为它们是关于允许编译器做什么的,而不是它每次都做的事情,因为当它们失败时,当线程以错误的顺序命中时,你将非常难以重现间歇性的错误。
答案 0 :(得分:6)
ISO 23270:2006 — Information technology—Programming languages—C#,§10.10说(我引用):
10.10执行顺序 执行应继续进行,以便每个执行线程的副作用 保留在关键执行点。定义了副作用 作为易失性字段的读或写,写入非易失性变量, 写入外部资源,抛出异常。 这些副作用的顺序的关键执行点 应保留对volatile字段的引用(第17.4.3节),
lock
语句(第15.12节),以及线程创建和终止。 一个实现可以自由地改变C#程序的执行顺序, 受以下限制条件限制:
数据依赖性保留在执行的线程中。 也就是说,计算每个变量的值,就像所有语句一样 在线程中以原始程序顺序执行。 (强调我的)。
保留初始化排序规则(§17.4.4,§17.4.5)。
对于易失性读取,保留了副作用的顺序 并写(第17.4.3节)。另外,实现不需要评估部分 表达式,如果它可以推断出该表达式的值未被使用而且没有 产生所需的副作用(包括通过调用方法或引起的任何副作用) 访问volatile字段)。程序执行被异步中断时 事件(例如由另一个线程抛出的异常),它不能保证 可观察的副作用在原始程序顺序中可见。
其他CLI标准同样可从ISO
获取 gratis但如果你担心多线程问题,你需要深入研究标准并理解关于原子性的规则。并非每项操作都必须是原子的。如果您是多线程并且调用引用除局部变量(例如,实例或类(静态)成员)之外的任何内容的方法而不通过lock
,互斥体,信号量或其他一些序列化技术序列化访问,那么让自己对竞争条件开放。
答案 1 :(得分:5)
我担心你甚至会问这个问题,但是因为你问过。
y = 10;
Thread.MemoryBarrier();
x = 5;
Thread.MemoryBarrier();
a = b + 10;
Thread.MemoryBarrier();
// ...
来自msdn
按如下方式同步内存访问:执行当前线程的处理器无法重新排序指令,以便在调用MemoryBarrier之前执行内存访问,然后在调用MemoryBarrier之后执行内存访问。
答案 2 :(得分:3)
您要找的是Thread.MemoryBarrier
但是,对于Microsoft当前的.NET实现,它们可能不是必需的。 See this SO question了解更多详情。
答案 3 :(得分:1)
在没有阅读有关.NET内存模型的任何内容的情况下,我可以向您保证.NET至少为您提供了这些保证(即锁定行为就像获取解锁一样),因为它们是最有用的最弱保证。 / p>