C#强制执行语句执行顺序

C#强制执行语句执行顺序,c#,java,.net,multithreading,synchronization,C#,Java,.net,Multithreading,Synchronization,我的问题是关于C#中执行保证的顺序(大概是.Net)。我给出了一些Java示例,我知道一些可以比较的东西 对于Java(来自“实践中的Java并发”) 不能保证一个线程中的操作将按照程序给定的顺序执行,只要在该线程中无法检测到重新排序,即使重新排序对其他线程来说是明显的 那么代码呢 y = 10; x = 5; a = b + 10; 在分配y=10之前,可以实际分配a=b+10 和Java(来自同一本书) 当线程A启动由同一个锁保护的同步块时,它在同步块中或之前所做的一切对线程B

我的问题是关于C#中执行保证的顺序(大概是.Net)。我给出了一些Java示例,我知道一些可以比较的东西

对于Java(来自“实践中的Java并发”)

不能保证一个线程中的操作将按照程序给定的顺序执行,只要在该线程中无法检测到重新排序,即使重新排序对其他线程来说是明显的

那么代码呢

  y = 10;
  x = 5;
  a = b + 10;
在分配y=10之前,可以实际分配a=b+10

和Java(来自同一本书)

当线程A启动由同一个锁保护的同步块时,它在同步块中或之前所做的一切对线程B都是可见的

在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;

我特别感兴趣的是一个答案,它可以提供一个明确的参考或一些其他真正有意义的理由,因为像这样的保证很难测试,因为它们是关于编译器可以做什么的,不是它每次都做的,因为当它们失败时,当线程以错误的顺序命中对象时,你将很难重现间歇性错误。

你要寻找的是


但是,它们对于Microsoft当前的.NET实现可能不是必需的。更多细节。

我担心你会问这个问题,但既然你问了

y = 10;
Thread.MemoryBarrier();
x = 5;
Thread.MemoryBarrier();
a = b + 10;
Thread.MemoryBarrier();
// ...

按如下方式同步内存访问:执行当前线程的处理器不能以这样的方式重新排列指令:在调用MemoryBarrier之前的内存访问在调用MemoryBarrier之后的内存访问之后执行

,§10.10规定(我引述):

10.10执行顺序 执行过程应确保每个执行线程的副作用 保存在关键执行点。定义了副作用 作为对易失性字段的读取或写入,对非易失性变量的写入, 对外部资源的写入,以及引发异常。 这些副作用的顺序所处的关键执行点 应保留对易失性字段的引用(§17.4.3),
lock
语句(§15.12),以及线程创建和终止。 实现可以自由更改C#程序的执行顺序, 受以下限制:

  • 数据依赖性保留在执行线程中。 也就是说,每个变量的值都是像所有语句一样计算的 在中,线程按原始程序顺序执行。(强调我的)

  • 保留初始化顺序规则(§17.4.4、§17.4.5)

  • 对于易失性读取,保留副作用的顺序 并写入(§17.4.3)。此外,一个实现不需要评估 表达式,如果它可以推断该表达式的值未被使用,并且 产生所需的副作用(包括调用方法或 访问易失性字段)。当程序执行被异步进程中断时 事件(例如由另一个线程引发的异常),不能保证 可观察到的副作用在原始程序顺序中是可见的

其他CLI标准同样可从ISO免费获得,网址为


但是,如果您担心多线程问题,则需要深入研究标准并理解原子性规则。并非所有的操作都是原子操作。如果您是多线程的,并且调用的方法只引用局部变量(例如,实例或类(静态)成员),而不通过
lock
、互斥体、信号量或其他序列化技术序列化访问,您将自己置于竞争条件下。

在没有阅读任何关于.NET内存模型的内容的情况下,我可以向您保证.NET至少会为您提供这些保证(即锁的行为类似于获取,解锁类似于释放),因为它们是最薄弱的有用保证。

如果您有一个执行顺序重要的单元代码,您应该同步整个单元,而不是依赖编译器可能优化或可能不优化的内容。最终的资源是CLI规范,它指定了.net受约束的内存模型和执行模型。您对第二段代码的结论并不是从第二本书的引用中得出的。除非y x和a是易变的,否则线程内的操作顺序不会与其他线程中的内存可见性耦合。此外(至少在旧版本的java中),同步块不能保证这一点,因为它在进入同步块时只有内存障碍,而在离开时没有。因此,为什么java上的双重检查锁定被破坏了。我不知道这在更新的版本中是否发生了变化,就像我上次在2003年左右看到的那样。因此,要注意内存障碍在哪里,如果事情已经释放而不能获得语义。在C语言语言规范中有几点需要考虑,这里可以看到:具体地,请看第3.10、8.12和105.3节,可能还有其他一些。