문제

나는의 행동을 시각적으로 설명하는 작은 프로그램을 코딩하고 싶습니다. volatile 예어. 이상적으로는 비 휘발성 정적 필드에 동시에 액세스하고 그로 인해 잘못된 동작을 수행하는 프로그램이어야합니다.

동일한 프로그램에서 휘발성 키워드를 추가하면 문제가 해결됩니다.

내가 달성하지 못한 것. 여러 번 시도하고 최적화를 가능하게하는 것도 '휘발성'키워드없이 항상 올바른 동작을 얻습니다.

이 주제에 대해 알고 있습니까? 간단한 데모 앱에서 이러한 문제를 시뮬레이션하는 방법을 알고 있습니까? 하드웨어에 의존합니까?

도움이 되었습니까?

해결책

나는 일하는 예를 달성했다!

Wiki로부터받은 주요 아이디어는 C#에 대한 약간의 변경 사항이 있습니다. Wiki 기사는 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 구현이 반드시 할 필요는없는 일을 허용합니다. 휘발성 키워드에서 내가 본 최고의 문서는 Joe Duffy 의이 블로그 게시물. 그는 MSDN 문서가 "오해의 소지가있다"고 말합니다.

'휘발성'키워드가 지정되지 않았을 때 실제로 발생하는 결함의 문제는 아니며, 지정되지 않았을 때 오류가 발생할 수 있습니다. 일반적으로 당신은 이것이 컴파일러보다 더 좋은시기를 알게 될 것입니다!

그것에 대해 가장 쉬운 방법은 컴파일러가 원할 경우 특정 값을 인라인 할 수 있다는 것입니다. 값을 휘발성으로 표시하면 자신과 컴파일러에게 값이 실제로 변경 될 수 있다고 말합니다 (컴파일러가 그렇게 생각하지 않더라도). 이는 컴파일러가 인라인 값을 내거나 캐시를 유지하거나 값을 조기에 읽지 않아야 함을 의미합니다 (최적화 시도).

이 동작은 C ++와 같은 키워드가 아닙니다.

MSDN에는 간단한 설명이 있습니다 여기. 다음은 아마도 주제에 대한 더 깊이있는 게시물입니다. 변동성, 원자력 및 연동

코드가 가상 머신에 의해 추상화되므로 C#에서 시연하기는 어렵 기 때문에이 시스템의 한 구현에서는 휘발성없이 작동하지만 다른 시스템에서는 실패 할 수 있습니다.

Wikipedia는 C에서 그것을 보여주는 방법이 좋은 예를 가지고 있습니다.

JIT 컴파일러가 변수의 값이 어쨌든 변경 될 수 없다고 결정하면 더 이상 확인하지 않는 기계 코드를 생성하는 경우 C#에서도 동일한 일이 발생할 수 있습니다. 이제 다른 스레드가 값을 변경한다면 첫 번째 스레드가 여전히 루프에 잡힐 수 있습니다.

또 다른 예는 바쁘다.

다시 말하지만, 이것은 C#에서도 발생할 수 있지만 가상 머신과 JIT 컴파일러 (또는 JIT가없는 경우 통역사 ... 이론적으로는 MS는 항상 JIT 컴파일러를 사용하고 모노를 사용한다고 생각합니다. 하나; 그러나 수동으로 비활성화 할 수 있습니다).

이 행동에 대한 집단적 이해에 대한 나의 기여는 다음과 같습니다. 그것은 그다지 많지 않습니다. 그것은 휘발성 구절의 행동을 비 휘발성 (예 : "정상") int 값, 나란히 보여주는 시연 (XKIP의 데모 기반) 일뿐입니다. -사이드, 같은 프로그램에서 ...이 스레드를 찾을 때 내가 찾고 있던 것입니다.

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 그것).

건배, 모든 물고기에 감사드립니다. 키이스.


추신: 나는 원래 요청의 데모에 매우 관심이있을 것입니다. 공전 휘발성 int 어디에서 올바르게 행동합니다 공전 int 오작동.

나는이 도전을 시도하고 실패했다. (실제로 나는 꽤 빨리 포기했다 ;-). 내가 정적 vars로 시도한 모든 것에서 그들은 그들이 "올바르게"행동하는지 여부에 관계없이 행동합니다. 휘발성 물질 ... 그리고 나는 그것이 왜 그런지에 대한 설명을 좋아합니다. 실제로 그렇다면 ... 컴파일러가 캐시하지 않는 것입니까? 가치 레지스터의 정적 변수 (즉, 캐시 a 참조 대신 그 힙 주소)?

아니요 이것은 새로운 질문이 아닙니다 ... 커뮤니티를 막으려는 시도입니다. 원래 질문에.

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
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top