콘텐츠로 이동

C# 스레드 동기화 완전 가이드

가장 일반적인 동기화 방법입니다.

private readonly object _lock = new();
public void Add(int item) {
lock (_lock) {
_list.Add(item);
}
}

lockMonitor.Enter / Monitor.Exit의 문법적 설탕입니다. finally로 항상 해제됩니다.

private readonly object _lock = new();
private Queue<int> _queue = new();
public void Produce(int item) {
lock (_lock) {
_queue.Enqueue(item);
Monitor.Pulse(_lock); // 대기 중인 스레드 하나 깨움
}
}
public int Consume() {
lock (_lock) {
while (_queue.Count == 0)
Monitor.Wait(_lock); // lock 해제 후 대기, 신호 받으면 재획득
return _queue.Dequeue();
}
}

SemaphoreSlim — 동시 접근 수 제한

섹션 제목: “SemaphoreSlim — 동시 접근 수 제한”
private readonly SemaphoreSlim _sem = new(3); // 최대 3개 동시 접근
async Task AccessResourceAsync() {
await _sem.WaitAsync();
try {
await DoWorkAsync();
} finally {
_sem.Release();
}
}

ReaderWriterLockSlim — 읽기/쓰기 분리

섹션 제목: “ReaderWriterLockSlim — 읽기/쓰기 분리”
private readonly ReaderWriterLockSlim _rwLock = new();
private Dictionary<string, string> _cache = new();
public string? Get(string key) {
_rwLock.EnterReadLock();
try { return _cache.TryGetValue(key, out var v) ? v : null; }
finally { _rwLock.ExitReadLock(); }
}
public void Set(string key, string value) {
_rwLock.EnterWriteLock();
try { _cache[key] = value; }
finally { _rwLock.ExitWriteLock(); }
}

읽기가 압도적으로 많고 쓰기가 드문 캐시, 설정 저장소에 적합합니다.

Interlocked — 락 없는 원자적 연산

섹션 제목: “Interlocked — 락 없는 원자적 연산”
private int _counter = 0;
// 원자적 증가
int newVal = Interlocked.Increment(ref _counter);
// 비교 후 교환 (CAS)
int original = Interlocked.CompareExchange(ref _counter, newValue, expectedValue);
// 원자적 읽기/쓰기 (64비트)
long val = Interlocked.Read(ref _longValue);
Interlocked.Exchange(ref _longValue, newValue);
// 단일 인스턴스 보장
using var mutex = new Mutex(true, "Global\\MyApp", out bool createdNew);
if (!createdNew) {
Console.WriteLine("이미 실행 중");
return;
}
// 앱 실행...

Mutex는 OS 수준 동기화로 프로세스 간 공유 가능합니다. 단일 프로세스 내에서는 lock이 훨씬 가볍습니다.

private volatile bool _running = true;
void Worker() {
while (_running) { } // volatile 없으면 최적화로 루프 탈출 안 될 수 있음
}
void Stop() { _running = false; }

volatile은 캐싱 없이 메모리에서 직접 읽기/쓰기를 보장합니다. Interlockedlock보다 약한 보장이지만 단순 플래그에 사용할 수 있습니다.

기법사용 상황
lock단순 임계 구역
Monitor.Wait/Pulse조건 대기가 있는 Producer-Consumer
SemaphoreSlim동시 접근 수 제한, async 지원 필요
ReaderWriterLockSlim읽기 多 / 쓰기 少
Interlocked단순 카운터, CAS 패턴
Mutex프로세스 간 동기화, 단일 인스턴스
  • 가능한 한 lock을 사용하고, 필요할 때 전문화된 기법으로 이동
  • SemaphoreSlimasync/await와 함께 사용 가능한 유일한 동기화 기본체
  • Interlocked로 카운터/플래그는 락 없이 처리
  • 데드락 방지: 항상 같은 순서로 락 획득, 최소한의 구간만 락 유지