gpt4 book ai didi

c# - 为什么即使使用 Monitor 时,并非所有成员变量都需要 volatile 以保证线程安全? (为什么这个模型真的有效?)

转载 作者:行者123 更新时间:2023-12-02 00:29:32 24 4
gpt4 key购买 nike

(我知道他们没有,但我正在寻找这实际上在不使用 volatile 的情况下工作的根本原因,因为应该没有什么可以阻止编译器在没有 volatile 的情况下将变量存储在寄存器中......或者是否存在...... .)

这个问题源于思想的不一致,即如果没有 volatile ,编译器(理论上可以以各种方式优化任何变量,包括将其存储在 CPU 寄存器中)。虽然文档说在使用诸如变量锁之类的同步时不需要这样做。但在某些情况下,编译器/jit 似乎无法知道您是否会在代码路径中使用它们。因此怀疑这里确实发生了其他事情以使内存模型“起作用”。

在此示例中,是什么阻止编译器/jit 将 _count 优化到寄存器中,从而在寄存器上完成增量而不是直接写入内存(稍后在退出调用后写入内存)?如果_count是 volatile 的,那么似乎一切都应该没问题,但是很多代码都是在没有 volatile 的情况下编写的。如果编译器在方法中看到锁或同步对象,那么编译器可能知道不要将 _count 优化到寄存器中,这是有道理的。但在这种情况下,锁调用位于另一个函数中。

大多数文档都说,如果您使用像锁这样的同步调用,则不需要使用 volatile。

那么是什么阻止编译器将 _count 优化到寄存器中并可能仅更新锁内的寄存器呢?我有一种感觉,由于这个确切的原因,大多数成员变量不会被优化到寄存器中,因为每个成员变量确实需要是 volatile 的,除非编译器可以告诉它不应该优化(否则我怀疑大量代码会失败) 。几年前,我在查看 C++ 时看到了类似的内容,局部函数变量存储在寄存器中,而类成员变量则不然。

所以主要问题是,编译器/jit 不会将类成员变量放入寄存器中,因此 volatile 是不必要的,这真的是在没有 volatile 的情况下工作的唯一方法吗?

(请忽略调用中缺乏异常处理和安全性的问题,但您明白了要点。)

public class MyClass
{
object _o=new object();

int _count=0;

public void Increment()
{
Enter();
// ... many usages of count here...
count++;
Exit();
}




//lets pretend these functions are too big to inline and even call other methods
// that actually make the monitor call (for example a base class that implemented these)
private void Enter() { Monitor.Enter(_o); }
private void Exit() { Monitor.Exit(_o); } //lets pretend this function is too big to inline
// ...
// ...
}

最佳答案

进入和离开监视器会导致内存栅栏已满。因此,CLR 确保 Monitor.Enter/Monitor.Exit 之前的所有写入操作对所有其他线程都可见,并且方法调用之后的所有读取操作“发生”在它之后。这也意味着调用之前的语句不能在调用之后移动,反之亦然。

参见http://www.albahari.com/threading/part4.aspx .

关于c# - 为什么即使使用 Monitor 时,并非所有成员变量都需要 volatile 以保证线程安全? (为什么这个模型真的有效?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25827753/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com