c\#단일 모드(Singleton)의 6 가지 실현

16120 단어 c#단일 모드
요약
 우리 의 일상적인 업무 에서 응용 프로그램 에서 유일한 인 스 턴 스 를 유지 해 야 한다.예 를 들 어 IO 처리,데이터 베이스 작업 등 이다.이런 대상 들 은 모두 중요 한 시스템 자원 을 점용 해 야 하기 때문에 우 리 는 이러한 인 스 턴 스 의 생 성 을 제한 하거나 항상 공용 인 스 턴 스 를 사용 해 야 한다.이것 이 바로 우리 가 오늘 소개 하고 자 하 는―단일 모델(Singleton)이다.
 사용 빈도
단일 모드(Singleton):하나의 인 스 턴 스 만 있 고 전체 방문 점 을 제공 합 니 다.
본문

그림 1 단일 모드(Singleton)구성 도
단일 모드(Singleton)는 몇 개의 생 성 모드 에서 가장 대립 되 는 것 입 니 다.그 주요 특징 은 사용자 프로그램 호출 에 따라 새로운 인 스 턴 스 를 만 드 는 것 이 아니 라 특정한 유형의 인 스 턴 스 유일 성 을 제어 하 는 것 입 니 다.위의 그림 을 통 해 우 리 는 그 가 포함 하 는 역할 이 하나 밖 에 없다 는 것 을 알 수 있 습 니 다.바로 Singleton 입 니 다.이것 은 개인 구조 함 수 를 가지 고 있 습 니 다.이것 은 사용자 가 new 를 통 해 직접 인 스 턴 스 를 할 수 없 도록 합 니 다.그 밖 에 이 모드 에는 정적 개인 구성원 변수 인 스 턴 스 와 정적 공유 방법 인 인 인 스 턴 스()가 포함 되 어 있 습 니 다.인 스 턴 스()방법 은 자신 을 검증 하고 예화 한 다음 정적 구성원 변수 에 저장 하여 하나의 인 스 턴 스 만 생 성 될 수 있 도록 합 니 다.

그림 2 단일 모드(Singleton)논리 모델
다음은 6 가지 서로 다른 단일 모델(Singleton)의 실현 방식 을 소개 한다.이러한 실현 방식 은 모두 다음 과 같은 공통점 이 있다.
 1.개인 적 인 무 참 구조 함수 가 있 습 니 다.이것 은 다른 종류의 실례 화 를 방지 할 수 있 습 니 다.그리고 단일 클래스 도 계승 되 어 서 는 안 됩 니 다.만약 에 단일 클래스 가 계승 을 허용 한다 면 모든 하위 클래스 가 실례 를 만 들 수 있 습 니 다.이것 은 싱글 톤 모드 의'유일한 실례'의 취지 에 어 긋 납 니 다.
2.하나의 예 류 는 sealed 로 정의 되 는데 앞에서 언급 한 것 처럼 계승 되 어 서 는 안 되 기 때문에 보험 을 위해 이 종 류 를 파생 을 허용 하지 않 는 다 고 정의 할 수 있 지만 반드시 이렇게 정의 해 야 한다 고 요구 하지 않 습 니 다.
3.하나의 인 스 턴 스 인용 을 저장 하 는 정적 변 수 를 사용 합 니 다.
4.하나의 인 스 턴 스 의 인용 을 가 져 오 는 공유 정적 방법 입 니 다.인 스 턴 스 가 null 이면 하 나 를 만 듭 니 다.
버 전 1 스 레 드 가 안전 하지 않 습 니 다.

 /// <summary>
/// A simple singleton class implements.
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;

  /// <summary>
  /// Prevents a default instance of the 
  /// <see cref="Singleton"/> class from being created.
  /// </summary>
  private Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance
  {
    get { return _instance ?? (_instance = new Singleton()); }
  }
}

 이상 의 실현 방식 은 단일 스 레 드 환경 에 적 용 됩 니 다.다 중 스 레 드 환경 에서 Singleton 류 의 여러 인 스 턴 스 를 얻 을 수 있 기 때 문 입 니 다.만약 두 개의 라인 이 동시에 판단 된다 면
(null == _singleton),그리고 얻 은 결 과 는 진실 입 니 다.그러면 두 스 레 드 는 모두 Singleton 과 같은 인 스 턴 스 를 만 듭 니 다.그러면 Singleton 모드 의'유일한 인 스 턴 스'의 취지 에 어 긋 납 니 다.
 버 전 2 스 레 드 보안

 /// <summary>
/// A thread-safe singleton class.
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;
  private static readonly object SynObject = new object();

  Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance
  {
    get
    {
      // Syn operation.
      lock (SynObject)
      {
        return _instance ?? (_instance = new Singleton());
      }
    }
  }
}

상기 방식 의 실현 방식 은 스 레 드 가 안전 합 니 다.먼저 우 리 는 정적 으로 읽 기 전용 프로 세 스 보조 대상 을 만 들 었 습 니 다.lock 은 하나의 스 레 드 가 코드 의 임계 구역 에 있 을 때 다른 스 레 드 가 임계 구역 에 들 어 갈 수 없 도록 확보 하기 때 문 입 니 다(동기 화 작업).다른 스 레 드 가 잠 긴 코드 에 들 어 가 려 고 하면 대상 이 풀 릴 때 까지 기다 릴 것 입 니 다.다 중 스 레 드 에서 여러 개의 대상 인 스 턴 스 를 만 들 지 않도록 합 니 다.다만 이런 실현 방식 은 동기 화 작업 을 해 야 하 는데 이것 은 시스템 성능 에 영향 을 주 는 병목 이 고 추가 비용 이 증가 할 것 이다.
 Double-Checked Locking
앞에서 말 한 스 레 드 안전 실현 방식 의 문 제 는 동기 화 작업 을 하 는 것 입 니 다.그러면 우 리 는 조작 을 통과 하 는 횟수 를 낮 출 수 있 습 니까?사실 우 리 는 동기 화 작업 을 하기 전에 이 인 스 턴 스 가 null 인지 아 닌 지 를 판단 하면 조작 을 통과 하 는 횟수 를 낮 출 수 있 습 니 다.이것 은 전형 적 인 Double-Checked Locking 방법 입 니 다.

 /// <summary>
/// Double-Checked Locking implements a thread-safe singleton class
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;
  // Creates an syn object.
  private static readonly object SynObject = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      // Double-Checked Locking
      if (null == _instance)
      {
        lock (SynObject)
        {
          if (null == _instance)
          {
            _instance = new Singleton();
          }
        }
      }
      return _instance;
    }
  }
}

 네 번 째 실현 방식 을 소개 하기 전에 필드 가 beforefieldinit 형식 으로 표 시 될 때 이 필드 초기 화 는 언제든지 필드 가 인용 되 기 전에 발생 할 수 있 습 니 다.이 말 은 듣 기 에 좀 어색 하 니,이어서 구체 적 인 예 를 통 해 소개 합 시다.

 /// <summary>
/// Defines a test class.
/// </summary>
class Test
{
  public static string x = EchoAndReturn("In type initializer");

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}

위 에서 우 리 는 정적 필드 와 방법 을 포함 하 는 클래스 Test 를 정 의 했 지만 정적 구조 함 수 를 정의 하지 않 았 음 을 주의해 야 한다.

그림 3 Test 클래스 의 IL 코드

class Test
{
  public static string x = EchoAndReturn("In type initializer");

  // Defines a parameterless constructor.
  static Test()
  {
  }

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}

위 에서 우 리 는 Test 류 에 정적 구조 함 수 를 추가 합 니 다.

그림 4 Test 클래스 의 IL 코드
위의 Test 클래스 의 IL 코드 차 이 를 통 해 Test 클래스 가 정적 필드 를 포함 하고 정적 구조 함수 가 정의 되 지 않 았 을 때 이 클래스 는 beforefieldinit 로 표 시 됩 니 다.
지금 쯤"beforefieldinit 로 표 시 된 것 과 표시 되 지 않 은 것 은 어떤 차이 가 있 습 니까?"라 고 물 을 수도 있 습 니 다.OK.이제 다음 의 구체 적 인 예 를 통 해 차이 점 을 살 펴 보 자!

 class Test
{
  public static string x = EchoAndReturn("In type initializer");

  static Test()
  {
  }

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}

class Driver
{
  public static void Main()
  {
    Console.WriteLine("Starting Main");
    // Invoke a static method on Test
    Test.EchoAndReturn("Echo!");
    Console.WriteLine("After echo");
    Console.ReadLine();

    // The output result:
    // Starting Main
    // In type initializer
    // Echo!
    // After echo      
  }
}

나 는 모두 가 답 을 얻 을 수 있 을 것 이 라 고 믿는다.만약 에 EchoAndReturn()방법 을 호출 하기 전에 정적 구성원 의 초기 화 를 완성 해 야 하기 때문에 최종 출력 결 과 는 다음 과 같다.

그림 5 출력 결과
 이어서 우 리 는 Main()방법 에 string y=Test.x 를 추가 합 니 다.다음 과 같 습 니 다.

public static void Main()
{
  Console.WriteLine("Starting Main");
  // Invoke a static method on Test
  Test.EchoAndReturn("Echo!");
  Console.WriteLine("After echo");

  //Reference a static field in Test
  string y = Test.x;
  //Use the value just to avoid compiler cleverness
  if (y != null)
  {
    Console.WriteLine("After field access");
  }
  Console.ReadKey();

  // The output result:
  // In type initializer
  // Starting Main
  // Echo!
  // After echo
  // After field access

}


그림 6 출력 결과
위의 출력 결 과 를 통 해 정적 필드 의 초기 화가 정적 방법 으로 호출 되 기 전에 Wo 는 상상 하기 어렵 습 니 다!
마지막 으로 우 리 는 Test 클래스 에 정적 구조 함 수 를 다음 과 같이 추가 합 니 다.

 class Test
{
  public static string x = EchoAndReturn("In type initializer");

  static Test()
  {
  }

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}
 


그림 7 출력 결과
이론 적 으로 type initializer 는'에코!'에서 발생 해 야 한다.이후"After echo"이전 에는 유일 하지 않 은 결과 가 나 왔 습 니 다.Test 류 가 정적 구조 함 수 를 포함 할 때 만 type initializer 의 초기 화 를 확보 할 수 있 습 니 다."Echo!"그 다음 에 애 프 터 에코 랑
그 러 니까 type initializer 가 필드 에 인용 되 었 을 때 이 클래스 에 정적 구조 함 수 를 추가 해 야 합 니 다.다음은 단일 모드 의 정적 방식 을 소개 합 니 다.
 정적 초기 화
 

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

  // Explicit static constructor to tell C# compiler
  // not to mark type as beforefieldinit
  static Singleton()
  {
  }

  /// <summary>
  /// Prevents a default instance of the 
  /// <see cref="Singleton"/> class from being created.
  /// </summary>
  private Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance
  {
    get
    {
      return _instance;
    }
  }
}

상기 방식 의 실현 은 이전에 소개 한 방식 보다 간단 하지만 다 중 스 레 드 환경 에서 C\#실 현 된 Singleton 의 방식 이다.이러한 정적 초기 화 방식 은 자신의 필드 가 인용 되 었 을 때 만 예화 되 기 때문이다.
 IL 코드 를 통 해 정적 초기 화 를 분석 합 니 다.

그림 8 정적 초기 화 IL 코드
우선,여 기 는 beforefieldinit 의 수정자 가 없습니다.정적 구조 함 수 를 추 가 했 기 때문에 정적 필드 가 인용 되 었 을 때 초기 화 되 었 습 니 다.따라서 많은 스 레 드 가 참조 하려 고 하 더 라 도instance,정적 구조 함수 가 실행 되 고 정적 구성원 을instance 실례 화 후 사용 할 수 있 습 니 다.
 초기 화 지연

 /// <summary>
/// Delaies initialization.
/// </summary>
public sealed class Singleton
{
  private Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance { get { return Nested._instance; } }

  private class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
    }

    internal static readonly Singleton _instance = new Singleton();
  }
}

여기 서 우 리 는 초기 화 작업 을 Nested 클래스 의 정적 구성원 에 게 맡 겨 서 지연 초기 화 를 실현 합 니 다.
 Lazy type

 /// <summary>
/// .NET 4's Lazy<T> type
/// </summary>
public sealed class Singleton
{
  private static readonly Lazy<Singleton> lazy =
    new Lazy<Singleton>(() => new Singleton());

  public static Singleton Instance { get { return lazy.Value; } }

  private Singleton()
  {
  }
}

 이 방식 은 간단 하고 성능 이 좋 으 며 인 스 턴 스 를 만 들 었 는 지 확인 하 는 속성 IsValueCreated 도 제공 합 니 다.
 구체 적 인 예
현재 단일 모드(Singleton)를 사용 하여 부하 분산 기 를 실현 합 니 다.먼저 서버 클래스 를 정의 합 니 다.서버 이름과 IP 주 소 는 다음 과 같 습 니 다.

 /// <summary>
/// Represents a server machine
/// </summary>
class Server
{
  // Gets or sets server name
  public string Name { get; set; }

  // Gets or sets server IP address
  public string IP { get; set; }
}

부하 분산 기 는 서버 에서 만 사용 할 수 있 도록 대상 인 스 턴 스 만 제공 하기 때문에 단일 모드(Singleton)를 사용 하여 이 부하 분산 기 를 실현 합 니 다.

 /// <summary>
/// The 'Singleton' class
/// </summary>
sealed class LoadBalancer
{
  private static readonly LoadBalancer _instance =
    new LoadBalancer();

  // Type-safe generic list of servers
  private List<Server> _servers;
  private Random _random = new Random();

  static LoadBalancer()
  {
  }

  // Note: constructor is 'private'
  private LoadBalancer()
  {
    // Load list of available servers
    _servers = new List<Server> 
      { 
       new Server{ Name = "ServerI", IP = "192.168.0.108" },
       new Server{ Name = "ServerII", IP = "192.168.0.109" },
       new Server{ Name = "ServerIII", IP = "192.168.0.110" },
       new Server{ Name = "ServerIV", IP = "192.168.0.111" },
       new Server{ Name = "ServerV", IP = "192.168.0.112" },
      };
  }

  /// <summary>
  /// Gets the instance through static initialization.
  /// </summary>
  public static LoadBalancer Instance
  {
    get { return _instance; }
  }


  // Simple, but effective load balancer
  public Server NextServer
  {
    get
    {
      int r = _random.Next(_servers.Count);
      return _servers[r];
    }
  }
}

 위 에 부하 분산 기 류 LoadBalancer 는 정적 초기 화 방식 으로 단일 모드(Singleton)를 실현 합 니 다.

 static void Main()
{
  LoadBalancer b1 = LoadBalancer.Instance;
  b1.GetHashCode();
  LoadBalancer b2 = LoadBalancer.Instance;
  LoadBalancer b3 = LoadBalancer.Instance;
  LoadBalancer b4 = LoadBalancer.Instance;

  // Confirm these are the same instance
  if (b1 == b2 && b2 == b3 && b3 == b4)
  {
    Console.WriteLine("Same instance
"); } // Next, load balance 15 requests for a server LoadBalancer balancer = LoadBalancer.Instance; for (int i = 0; i < 15; i++) { string serverName = balancer.NextServer.Name; Console.WriteLine("Dispatch request to: " + serverName); } Console.ReadKey(); }

그림 9 LoadBalancer 출력 결과
 1.1.3 총화
단일 모드 의 장점:
단일 모드(Singleton)는 인 스 턴 스 대상 의 수 를 제어 하여 방문 대상 의 유일 성 을 확보 합 니 다.
1.인 스 턴 스 제어:단일 모드 는 다른 대상 이 자신 에 대한 실례 화 를 방지 하고 모든 대상 이 하나의 인 스 턴 스 에 접근 하도록 확보한다.
2.신축성:클래스 자체 가 실례 화 프로 세 스 를 제어 하기 때문에 클래스 는 실례 화 프로 세 스 를 바 꾸 는 데 상응하는 신축성 을 가진다.
 단일 모드 의 단점:
1.시스템 비용.이 시스템 의 비용 은 매우 작 아 보이 지만,이러한 인 스 턴 스 를 인용 할 때마다 인 스 턴 스 가 존재 하 는 지 확인 해 야 한다.이 문 제 는 정적 인 실례 를 통 해 해결 할 수 있다.
2.개발 혼동.하나의 사례 모델 의 대상 을 사용 할 때(특히 라 이브 러 리 에 정 의 된)개발 자 는 new 키 워드 를 사용 하여 대상 을 예화 할 수 없다 는 것 을 기억 해 야 한다.개발 자 들 은 라 이브 러 리 에 있 는 소스 코드 를 볼 수 없 기 때문에 하나의 종 류 를 예화 할 수 없다 는 것 을 알 게 되면 놀 랄 것 입 니 다.
3.대상 수명 주기.단일 모델 은 대상 의 소각 을 제기 하지 않 았 다.메모리 관 리 를 제공 하 는 개발 언어(예 를 들 어.NetFramework 기반 언어)에 서 는 하나의 모델 대상 만 이 대상 인 스 턴 스 를 없 앨 수 있 습 니 다.인 스 턴 스 에 대한 참조 가 있 기 때 문 입 니 다.각종 개발 언어 에서 예 를 들 어 C++,다른 종 류 는 대상 의 인 스 턴 스 를 없 앨 수 있 지만 이렇게 하면 단일 클래스 내부 의 지침 이 불분명 하 게 될 것 이다. 
단 례 적용 성
Singleton 모드 를 사용 하 는 데 필요 한 조건 이 있 습 니 다.한 시스템 이 하나의 인 스 턴 스 만 을 요구 할 때 하나의 인 스 턴 스 모드 를 사용 해 야 합 니 다.반면 하나의 클래스 가 몇 개의 인 스 턴 스 가 공존 할 수 있다 면 단일 모드 를 사용 하지 마 세 요.
단일 모드 로 전역 변 수 를 액세스 하지 마 십시오.이것 은 단일 모드 의 의도 에 어 긋 나 므 로 대응 하 는 정적 구성원 에 넣 는 것 이 좋 습 니 다.
데이터 베 이 스 를 하나의 예 로 연결 하지 마 세 요.한 시스템 이 데이터 베이스 와 여러 개의 연결 이 있 을 수 있 고 연결 탱크 가 있 는 상황 에서 가능 한 한 신속하게 연결 을 풀 어야 하기 때 문 입 니 다.Singleton 모드 는 정적 구성원 저장 클래스 의 인 스 턴 스 를 사용 하기 때문에 자원 이 제때에 방출 되 지 못 하고 문 제 를 가 져 올 수 있 습 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기