volatile
키워드 의 동작을 시각적으로 보여주는 작은 프로그램을 코딩하고 싶습니다 . 이상적으로는 비 휘발성 정적 필드에 대한 동시 액세스를 수행하고 그로 인해 잘못된 동작을하는 프로그램이어야합니다.
같은 프로그램에 volatile 키워드를 추가하면 문제가 해결됩니다.
내가 달성하지 못한 것. 여러 번 시도하여 최적화 등을 활성화해도 ‘volatile’키워드없이 항상 올바른 동작을 얻습니다.
이 주제에 대해 알고 있습니까? 간단한 데모 앱에서 이러한 문제를 시뮬레이션하는 방법을 알고 있습니까? 하드웨어에 의존합니까?
답변
나는 실제적인 예를 얻었습니다!
위키에서받은 주요 아이디어이지만 C #에 대한 일부 변경 사항이 있습니다. 위키 기사는 C ++의 정적 필드에 대해 이것을 보여줍니다. C #은 항상 정적 필드에 대한 요청을 신중하게 컴파일하는 것처럼 보이며 비 정적 필드로 예제를 만듭니다.
이 예제를 릴리스 모드 에서 디버거없이 (예 : Ctrl + F5 사용) 실행하면 줄 while (test.foo != 255)
이 ‘while (true)’로 최적화되고이 프로그램은 반환되지 않습니다. 그러나 volatile
키워드 를 추가 하면 항상 ‘OK’가 나타납니다.
class Test
{
/*volatile*/ int foo;
static void Main()
{
var test = new Test();
new Thread(delegate() { Thread.Sleep(500); test.foo = 255; }).Start();
while (test.foo != 255) ;
Console.WriteLine("OK");
}
}
답변
예, 하드웨어에 따라 다르지만 (여러 프로세서 없이는 문제가 발생하지 않을 수 있음) 구현에 따라 다릅니다. CLR 사양의 메모리 모델 사양은 CLR의 Microsoft 구현이 반드시 수행 할 필요는없는 작업을 허용합니다.
답변
‘volatile’키워드가 지정되지 않았을 때 발생하는 오류가 아니라 지정되지 않은 경우 오류가 발생할 수 있습니다. 일반적으로 이것이 컴파일러보다 더 나은 경우를 알게 될 것입니다!
그것에 대해 생각하는 가장 쉬운 방법은 컴파일러가 원하는 경우 특정 값을 인라인 할 수 있다는 것입니다. 값을 휘발성으로 표시함으로써 자신과 컴파일러에게 값이 실제로 변경 될 수 있음을 알리는 것입니다 (컴파일러가 그렇게 생각하지 않더라도). 즉, 컴파일러가 값을 인라인하거나 캐시를 유지하거나 값을 일찍 읽어서는 안됩니다 (최적화를 시도하기 위해).
이 동작은 실제로 C ++에서와 동일한 키워드가 아닙니다.
MSDN에는 여기에 간단한 설명이 있습니다 . 다음은 변동성, 원 자성 및 연동 에 대한보다 심층적 인 게시물입니다.
답변
코드가 가상 머신에 의해 추상화 되었기 때문에 C #으로 설명하기가 어렵습니다. 따라서이 머신의 한 구현에서는 휘발성없이 올바르게 작동하지만 다른 머신에서는 실패 할 수 있습니다.
위키 백과에는 C로 시연하는 방법에 대한 좋은 예가 있습니다.
JIT 컴파일러가 변수의 값이 어쨌든 변경 될 수 없다고 판단하여 더 이상 확인하지 않는 기계어 코드를 생성하면 C #에서도 동일한 일이 발생할 수 있습니다. 이제 다른 스레드가 값을 변경하는 경우 첫 번째 스레드가 여전히 루프에 걸릴 수 있습니다.
다시 말하지만, 이것은 C #에서도 발생할 수 있지만 가상 머신과 JIT 컴파일러 (또는 JIT가없는 경우 인터프리터 … 이론 상으로는 MS가 항상 JIT 컴파일러를 사용하고 Mono도 사용한다고 생각합니다. 하지만 수동으로 비활성화 할 수 있습니다.)
답변
이 동작에 대한 집단적 이해에 대한 저의 공헌은 다음과 같습니다. 휘발성 구절의 동작을 비 휘발성 (즉, “정상”) int 값과 나란히 표시하는 데모 (xkip 데모 기반)에 불과합니다. -side, 같은 프로그램에서 …이 스레드를 발견했을 때 찾던 것입니다.
using System;
using System.Threading;
namespace VolatileTest
{
class VolatileTest
{
private volatile int _volatileInt;
public void Run() {
new Thread(delegate() { Thread.Sleep(500); _volatileInt = 1; }).Start();
while ( _volatileInt != 1 )
; // Do nothing
Console.WriteLine("_volatileInt="+_volatileInt);
}
}
class NormalTest
{
private int _normalInt;
public void Run() {
new Thread(delegate() { Thread.Sleep(500); _normalInt = 1; }).Start();
// NOTE: Program hangs here in Release mode only (not Debug mode).
// See: http://stackoverflow.com/questions/133270/illustrating-usage-of-the-volatile-keyword-in-c-sharp
// for an explanation of why. The short answer is because the
// compiler optimisation caches _normalInt on a register, so
// it never re-reads the value of the _normalInt variable, so
// it never sees the modified value. Ergo: while ( true )!!!!
while ( _normalInt != 1 )
; // Do nothing
Console.WriteLine("_normalInt="+_normalInt);
}
}
class Program
{
static void Main() {
#if DEBUG
Console.WriteLine("You must run this program in Release mode to reproduce the problem!");
#endif
new VolatileTest().Run();
Console.WriteLine("This program will now hang!");
new NormalTest().Run();
}
}
}
위의 몇 가지 훌륭한 간결한 설명과 훌륭한 참고 자료가 있습니다. 내 머리를 volatile
돌릴 수 있도록 도와 주신 모든 분들께 감사드립니다 ( volatile
내 첫 번째 본능이 어디에 있는지에 의존하지 않을만큼 충분히 lock
).
건배, 모든 물고기에 감사드립니다. 키스.
PS는 : “내가보고 싶어 : 나는 매우이었다 원래 요청의 데모에 관심이있을 것 정적 휘발성 INT 제대로 곳 행동 정적 INT의 무례한 행동을.
나는이 도전을 시도했지만 실패했습니다. (사실 나는 꽤 빨리 포기했다 ;-). 내가 정적 vars로 시도한 모든 것에서 휘발성 여부에 관계없이 “올바르게”동작합니다 . 그리고 실제로 그럴 경우 왜 그런지 설명하고 싶습니다. 컴파일러는 레지스터의 정적 변수 값 을 캐시하지 않습니다 (즉, 대신 해당 힙 주소에 대한 참조를 캐시 함 )?
아니 이것은 지역 사회 stear하기위한 시도이다 … 새로운 질문하지 않습니다 다시 원래의 질문을.
답변
나는 많은 도움을 준 Joe Albahari의 다음 텍스트를 발견했습니다.
정적 휘발성 필드를 생성하여 약간 변경 한 위의 텍스트에서 예제를 가져 왔습니다. volatile
키워드 를 제거하면 프로그램이 무기한 차단됩니다. 릴리스 모드 에서이 예제를 실행하십시오 .
class Program
{
public static volatile bool complete = false;
private static void Main()
{
var t = new Thread(() =>
{
bool toggle = false;
while (!complete) toggle = !toggle;
});
t.Start();
Thread.Sleep(1000); //let the other thread spin up
complete = true;
t.Join(); // Blocks indefinitely when you remove volatile
}
}