스레드 세이프 싱글톤
Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 52번째.
싱글톤 패턴(Singleton pattern)은 애플리케이션 전체에서 클래스의 인스턴스가 단 하나만 존재하도록 보장하며, 이에 대한 전역적인 접근 지점을 제공합니다. 이는 설정 정보, 로깅 서비스 또는 데이터베이스 연결과 같은 공유 리소스를 관리하는 데 유용합니다.
기본적인 싱글톤(Singleton)은 외부 인스턴스화를 방지하기 위해 private 생성자를 사용하고, 접근을 제공하기 위해 static 속성을 사용합니다:
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // Private constructor
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}lock 문은 스레드 안전성(thread safety)을 보장합니다. 여러 스레드가 동시에 Instance에 접근하려고 하면, 한 번에 하나의 스레드만 임계 구역(critical section)에 진입할 수 있습니다. 이것이 없으면, 두 스레드가 모두 _instance를 null로 보고 별도의 인스턴스를 생성할 수 있습니다.
C#은 정적 초기화를 사용하여 더 간단한 스레드 안전(thread-safe) 방식을 제공합니다:
public class Logger
{
private static readonly Logger _instance = new Logger();
private Logger() { }
public static Logger Instance => _instance;
public void Log(string message) => Console.WriteLine(message);
}이 버전은 CLR이 정적 필드 초기화가 단 한 번만 발생하도록 보장하기 때문에 스레드 안전(thread-safe)합니다. 두 방식 모두 항상 동일한 인스턴스를 얻을 수 있도록 보장합니다:
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");챌린지
쉬움싱글톤 패턴을 사용하여 스레드로부터 안전한 설정 관리자(configuration manager)를 만들어 보겠습니다. 실제 애플리케이션에서 설정 정보는 프로그램 전체에서 접근 가능해야 하며, 단 하나의 인스턴스만 존재하도록 보장해야 합니다. 이는 싱글톤 패턴을 적용하기에 완벽한 사례입니다.
코드는 두 개의 파일로 구성됩니다:
ConfigManager.cs:Configuration네임스페이스 안에 싱글톤 패턴을 구현하는ConfigManager클래스를 생성합니다. 설정 관리자는 다음 사항을 포함해야 합니다:- 단일 인스턴스를 유지하기 위한 private static 필드
- 외부에서 인스턴스화를 방지하기 위한 private 생성자
- 단일 인스턴스를 반환하는 static
Instance속성 (스레드 안전성을 위해 더 간단한 정적 초기화 방식을 사용하세요) "MyApp"으로 초기화된 private 문자열 필드_appName- 앱 이름을 업데이트하는
SetAppName(string name)메서드 - 현재 앱 이름을 반환하는
GetAppName()메서드
Program.cs: 메인 파일에서 인스턴스에 여러 번 접근하고 모든 참조에서 변경 사항이 유지되는 것을 보여줌으로써 싱글톤이 올바르게 작동함을 증명합니다.
하나의 입력을 받게 됩니다:
- 설정할 새로운 애플리케이션 이름 (예:
ProductionServer)
프로그램의 동작 순서:
ConfigManager인스턴스를 가져와 기본 앱 이름을 출력합니다.- 인스턴스를 다시 가져와(다른 변수에 저장) 입력받은 새로운 앱 이름을 설정하는 데 사용합니다.
- 인스턴스를 세 번째로 가져와 앱 이름을 출력하여 모든 참조가 동일한 데이터를 공유함을 증명합니다.
예를 들어, 입력이 ProductionServer인 경우 출력은 다음과 같아야 합니다:
MyApp
ProductionServer이는 싱글톤의 핵심적인 이점을 보여줍니다. Instance에 얼마나 많이 접근하든 관계없이 항상 상태를 공유하는 동일한 객체를 얻게 됩니다. 정적 초기화 방식은 CLR이 여러 스레드가 동시에 접근하더라도 정적 필드가 단 한 번만 초기화되도록 보장하기 때문에 스레드 안전성을 보장합니다.
치트 시트
싱글톤 패턴(Singleton pattern)은 애플리케이션 전체에서 클래스의 인스턴스가 단 하나만 존재하도록 보장하며, 이에 대한 전역적인 접근 지점을 제공합니다.
스레드 안전성(thread safety)을 위해 명시적 잠금(explicit locking)을 사용하는 기본 싱글톤:
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // 프라이빗 생성자
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}lock 문은 한 번에 하나의 스레드만 임계 구역(critical section)에 진입할 수 있도록 허용하여 스레드 안전성을 보장합니다.
정적 초기화(static initialization)를 사용한 더 간단한 스레드 안전 싱글톤:
public class Logger
{
private static readonly Logger _instance = new Logger();
private Logger() { }
public static Logger Instance => _instance;
public void Log(string message) => Console.WriteLine(message);
}CLR이 정적 필드 초기화가 단 한 번만 발생하도록 보장하기 때문에 이 방식은 스레드로부터 안전합니다.
사용법 - 항상 동일한 인스턴스를 반환합니다:
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");직접 해보기
using System;
using Configuration;
class Program
{
public static void Main(string[] args)
{
// 입력 읽기 - 새 애플리케이션 이름
string newAppName = Console.ReadLine();
// TODO: ConfigManager 인스턴스를 가져와서 기본 애플리케이션 이름을 출력하세요
// TODO: 인스턴스를 다시 가져와서(다른 변수에 저장) 새 애플리케이션 이름을 설정하는 데 사용하세요
// TODO: 인스턴스를 세 번째로 가져와서 애플리케이션 이름을 출력하여 모든 참조가 동일한 데이터를 공유하는지 확인하세요
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.