programing

C #에서 volatile 키워드의 사용법 설명

nasanasas 2020. 9. 12. 10:06
반응형

C #에서 volatile 키워드의 사용법 설명


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 사양의 메모리 모델 사양은 Microsoft의 CLR 구현에서 반드시 수행 할 필요는없는 작업을 허용합니다. volatile 키워드에 대해 내가 본 최고의 문서는 Joe Duffy의 블로그 게시물 입니다. 그는 MSDN 문서가 "매우 오해의 소지가있다"고 말합니다.


'volatile'키워드가 지정되지 않았을 때 발생하는 오류가 아니라 지정되지 않은 경우 오류가 발생할 수 있습니다. 일반적으로 이것이 컴파일러보다 더 나은 경우를 알게 될 것입니다!

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

이 동작은 실제로 C ++에서와 동일한 키워드가 아닙니다.

MSDN에는 여기에 간단한 설명이 있습니다 . 다음은 변동성, 원 자성 및 연동 에 대한보다 심층적 인 게시물입니다.


코드가 가상 머신에 의해 추상화 되었기 때문에 C #에서는 설명하기가 어렵습니다. 따라서이 머신의 한 구현에서는 휘발성없이 올바르게 작동하지만 다른 머신에서는 실패 할 수 있습니다.

위키 백과에는 C로 시연하는 방법에 대한 좋은 예가 있습니다.

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

또 다른 예는 Busy Waiting입니다.

다시 말하지만 이것은 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: I'd be very interested in a demo of the original request, which was: "I'd like to see a static volatile int behaving correctly where a static int misbehaves.

I have tried and failed this challenge. (Actually I gave up pretty quickly ;-). In everything I tried with static vars they behave "correctly" regardless of whether or not they're volatile ... and I'd love an explanation of WHY that is the case, if indeed it is the case... Is it that the compiler doesn't cache the values of static vars in registers (i.e. it caches a reference to that heap-address instead)?

아니 이것은 지역 사회 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
    }
}

참고 URL : https://stackoverflow.com/questions/133270/illustrating-usage-of-the-volatile-keyword-in-c-sharp

반응형