(저는 컴파일러가 volatile 변수없이 레지스터에 변수를 저장하는 것을 방해하지 않아야하므로 휘발성을 사용하지 않고 실제로 작동하는 근본적인 이유를 찾고 있습니다. ... 또는 거기에 ...)Monitor를 사용하는 경우에도 모든 멤버 변수가 스레드 안전성을 위해 휘발성이 필요한 것은 아닙니까? (모델이 실제로 작동하는 이유는 무엇입니까?)
이 질문은 volatile이 없으면 컴파일러가 이론적으로 CPU 레지스터에 저장하는 것을 포함하여 다양한 변수로 모든 변수를 최적화 할 수 있다는 생각의 불일치에서 유래합니다. 변수 주위의 잠금과 같은 동기화를 사용할 때는 필요하지 않습니다. 하지만 실제로 컴파일러/jit가 코드 경로에서 사용할지 여부를 알 수있는 방법이 없습니다. 그래서 의심은 메모리 모델을 "일"하게 만들기 위해 여기에서 실제로 일어나고있는 다른 것입니다.
이 예제에서는 컴파일러/jit가 _count를 레지스터로 최적화하지 못하게하여 메모리가 아닌 레지스터에서 증분이 완료되는 것을 막습니다 (나중에 종료 호출 후 메모리에 기록). _count가 휘발성이면 모든 것이 정상적으로 보일 수 있지만 많은 코드가 휘발성없이 작성됩니다. 컴파일러가 메서드에서 잠금 또는 동기화 객체를 찾으면 레지스터에 _count를 최적화하지 않을 수 있다는 것을 알 수 있습니다. 그러나이 경우 잠금 호출은 다른 함수에 있습니다.
대부분의 문서에서는 잠금과 같은 동기화 호출을 사용하는 경우 휘발성을 사용할 필요가 없다고 말합니다.
그렇다면 컴파일러가 _count를 레지스터로 최적화하지 못하도록하고 잠겨있는 레지스터를 잠재적으로 업데이트하지 못하게하는 이유는 무엇입니까? 필자는 컴파일러가 최적화해서는 안된다는 말을하지 않는 한, 모든 멤버 변수가 정말로 휘발성 일 필요가 있기 때문에 대부분의 멤버 변수가 레지스터에 최적화되지 않는다는 느낌이 들었습니다. 그렇지 않으면 많은 코드가 실패 할 것이라고 생각합니다. . 비슷한 것을 C++에서 보았을 때 몇 년 전에 로컬 함수 변수가 레지스터에 저장되었지만 클래스 멤버 변수는 저장되지 않았습니다.
주된 질문은 컴파일러/jit이 클래스 멤버 변수를 레지스터에 넣지 않아 휘발성이 없으면 휘발성이 작동하지 않는 유일한 방법일까요?
(호출에 예외 처리 및 안전의 부족을 무시하지만 요점을 보내 주시기 바랍니다.)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
// ...
// ...
}
20 개의 'MyClass' 객체를 인스턴스화하고 하드웨어에 8 개의 범용 레지스터 만 있으면 어떻게됩니까? – Mephy
[Sayonara volatile] (http://joeduffyblog.com/2010/12/04/sayonara-volatile/) –
이 동작을 요구하는 .NET 메모리 모델에는 내가 아는 것이 하나도 없습니다. 보수적 인 지터 옵티 마이저 디자인 일 뿐이므로 클래스 필드에 대한 쓰기는 항상 외부에서 관찰 할 수 있으며 내부 호출이 변수 상태에 영향을 줄 수 있다고 가정합니다. 이러한 가정을 깨뜨리는 지터는별로 인기가 없을 것입니다. –