C # 정적 생성자 스레드는 안전합니까?

다시 말해,이 싱글 톤 구현 스레드는 안전합니다.

public class Singleton
{
    private static Singleton instance;

    private Singleton() { }

    static Singleton()
    {
        instance = new Singleton();
    }

    public static Singleton Instance
    {
        get { return instance; }
    }
}



답변

정적 생성자는 클래스의 인스턴스를 만들거나 정적 멤버에 액세스하기 전에 응용 프로그램 도메인 당 한 번만 실행되도록 보장됩니다. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

제시된 구현은 초기 구성에 스레드 안전성이 있습니다. 즉, Singleton 오브젝트를 구성하는 데 잠금 또는 널 테스트가 필요하지 않습니다. 그러나 이것이 인스턴스 사용이 동기화된다는 의미는 아닙니다. 이 작업을 수행 할 수있는 다양한 방법이 있습니다. 나는 아래에 하나를 보여 주었다.

public class Singleton
{
    private static Singleton instance;
    // Added a static mutex for synchronising use of instance.
    private static System.Threading.Mutex mutex;
    private Singleton() { }
    static Singleton()
    {
        instance = new Singleton();
        mutex = new System.Threading.Mutex();
    }

    public static Singleton Acquire()
    {
        mutex.WaitOne();
        return instance;
    }

    // Each call to Acquire() requires a call to Release()
    public static void Release()
    {
        mutex.ReleaseMutex();
    }
}


답변

이 모든 답변이 동일한 일반적인 답변을 제공하지만 한 가지주의 사항이 있습니다.

일반 클래스의 모든 잠재적 파생은 개별 유형으로 컴파일됩니다. 따라서 제네릭 형식에 정적 생성자를 구현할 때는주의하십시오.

class MyObject<T>
{
    static MyObject()
    {
       //this code will get executed for each T.
    }
}

편집하다:

데모는 다음과 같습니다.

static void Main(string[] args)
{
    var obj = new Foo<object>();
    var obj2 = new Foo<string>();
}

public class Foo<T>
{
    static Foo()
    {
         System.Diagnostics.Debug.WriteLine(String.Format("Hit {0}", typeof(T).ToString()));
    }
}

콘솔에서 :

Hit System.Object
Hit System.String


답변

실제로 정적 생성자를 사용하여 입니다 스레드. 정적 생성자는 한 번만 실행됩니다.

C # 언어 사양에서 :

클래스의 정적 생성자는 지정된 응용 프로그램 도메인에서 최대 한 번 실행됩니다. 정적 생성자의 실행은 응용 프로그램 도메인 내에서 발생하는 다음 이벤트 중 첫 번째 이벤트에 의해 트리거됩니다.

  • 클래스의 인스턴스가 작성됩니다.
  • 클래스의 모든 정적 멤버가 참조됩니다.

따라서 싱글 톤이 올바르게 인스턴스화 될 것이라고 믿을 수 있습니다.

Zooba는 정적 생성자가 단일 스레드에 대한 스레드 안전 공유 액세스를 보장하지 않는다는 훌륭한 지적을했습니다. 다른 방법으로 처리해야합니다.


답변

C # singleton의 위 MSDN 페이지에있는 Cliffnotes 버전은 다음과 같습니다.

다음 패턴을 사용하십시오. 항상 잘못 될 수는 없습니다.

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get
      {
         return instance;
      }
   }
}

명백한 싱글 톤 기능 외에도 (C ++의 싱글 톤과 관련하여)이 두 가지를 무료로 제공합니다.

  1. 게으른 건설 (또는 호출되지 않은 경우 건설이 아님)
  2. 동기화

답변

정적 생성자는 App Domain 당 한 번만 실행되므로 접근 방식은 정상입니다. 그러나보다 간결한 인라인 버전과 기능상 차이가 없습니다.

private static readonly Singleton instance = new Singleton();

스레드 안전성은 느리게 초기화 할 때 더 중요한 문제입니다.


답변

스레드가 클래스에 액세스 하기 전에 정적 생성자가 실행 을 완료 합니다 .

    private class InitializerTest
    {
        static private int _x;
        static public string Status()
        {
            return "_x = " + _x;
        }
        static InitializerTest()
        {
            System.Diagnostics.Debug.WriteLine("InitializerTest() starting.");
            _x = 1;
            Thread.Sleep(3000);
            _x = 2;
            System.Diagnostics.Debug.WriteLine("InitializerTest() finished.");
        }
    }

    private void ClassInitializerInThread()
    {
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() starting.");
        string status = InitializerTest.Status();
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() status = " + status);
    }

    private void classInitializerButton_Click(object sender, EventArgs e)
    {
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
    }

위의 코드는 아래 결과를 생성했습니다.

10: ClassInitializerInThread() starting.
11: ClassInitializerInThread() starting.
12: ClassInitializerInThread() starting.
InitializerTest() starting.
InitializerTest() finished.
11: ClassInitializerInThread() status = _x = 2
The thread 0x2650 has exited with code 0 (0x0).
10: ClassInitializerInThread() status = _x = 2
The thread 0x1f50 has exited with code 0 (0x0).
12: ClassInitializerInThread() status = _x = 2
The thread 0x73c has exited with code 0 (0x0).

정적 생성자가 실행하는 데 오랜 시간이 걸렸지 만 다른 스레드는 중지되고 기다렸습니다. 모든 스레드는 정적 생성자의 맨 아래에 설정된 _x 값을 읽습니다.


답변

공용 언어 인프라 사양 을 보장하는 “명시 적으로 사용자 코드에 의해 호출하지 않는 한, 특정 유형에 대해 정확히 한 번 실행되어야 초기화 유형.” (9.5.3.1 절) 따라서 Singleton ::. cctor를 느슨하게 호출하는 데 약간의 괴상한 IL이 없으면 (아마도) Singleton 유형을 사용하기 전에 정적 생성자가 정확히 한 번만 실행되고 Singleton 인스턴스가 하나만 생성됩니다. 인스턴스 속성은 스레드로부터 안전합니다.

Singleton의 생성자가 Instance 속성에 액세스하더라도 (간접적으로도) Instance 속성은 null입니다. 가장 좋은 방법은 속성 접근 자에서 인스턴스가 null이 아닌지 확인하여이 문제가 발생하는 시점을 감지하고 예외를 throw하는 것입니다. 정적 생성자가 완료되면 Instance 속성은 null이 아닙니다.

으로 Zoomba의 답변 당신이 밖으로 포인트는 여러 스레드에서 액세스 싱글 안전을 만들거나 싱글 인스턴스를 사용하여 주위에 잠금 메커니즘을 구현해야합니다.