멀티스레드 환경에서는 여러 스레드가 동시에 같은 리소스에 접근할 수 있으므로 동기화(Synchronization)가 필수적입니다. 동기화를 적절히 활용하면 리소스 접근 순서를 제어하여 데이터 손상이나 경쟁 상태(race condition)를 예방할 수 있습니다.
C# 스레드(Thread)란?
스레드는 프로세스 내에서 실제 작업을 수행하는 실행 흐름의 최소 단위입니다. 하나의 프로세스는 여러 개의 스레드를 가질 수 있으며, 각 스레드는 독립적으로 코드를 실행합니다. C#에서는 System.Threading 네임스페이스를 통해 스레드를 생성하고 제어할 수 있습니다.
lock 문: 코드 블록의 원자성 보장
C#의 lock 문은 다른 스레드의 방해 없이 특정 코드 블록이 끝까지 실행되도록 보장하는 가장 기본적인 동기화 도구입니다. lock 블록에 진입하면 해당 객체에 대한 상호 배제 잠금(mutual-exclusion lock)이 획득되고, 블록이 종료되면 잠금이 자동으로 해제됩니다.
private readonly object lockObj = new object();
public void SafeMethod()
{
lock (lockObj)
{
// 한 번에 하나의 스레드만 실행할 수 있는 영역
}
}
Mutex 클래스: 프로세스 간 동기화
Mutex(뮤텍스)는 상호 배제를 위한 동기화 프리미티브(synchronization primitive)입니다. lock이 단일 프로세스 내에서만 유효한 것과 달리, Mutex는 여러 프로세스에 걸친 스레드 동기화에도 사용할 수 있습니다. 특정 코드 블록을 두 개 이상의 스레드가 동시에 실행하지 못하도록 차단하는 용도로 활용됩니다.
Mutex 생성 방법
private static Mutex m = new Mutex();
생성한 Mutex 객체는 WaitOne() 메서드로 소유권을 요청하고, ReleaseMutex() 메서드로 해제할 수 있습니다. 이름 있는(named) Mutex를 사용하면 서로 다른 프로세스 간에도 동일한 리소스 접근을 통제할 수 있습니다.
스레드 풀(ThreadPool) 활용하기
C#의 스레드 풀은 재사용 가능한 스레드들의 모음으로, 주로 백그라운드 작업을 처리하는 데 사용됩니다. 스레드가 하나의 작업을 완료하면 대기 중인 스레드들이 있는 큐로 반환되어 재사용됩니다. 이러한 방식은 매번 새 스레드를 생성하고 소멸시키는 비용을 줄여 전체 성능을 향상시킵니다.
스레드 풀 생성 방법
먼저 다음 네임스페이스를 추가합니다.
using System.Threading;
이후 ThreadPool 클래스의 QueueUserWorkItem 메서드를 호출하여 작업을 큐에 등록합니다.
ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
WaitCallback 델리게이트에는 실행할 메서드(예: Run)를 전달하며, 해당 메서드는 스레드 풀의 가용 스레드에서 비동기적으로 실행됩니다.
마무리
정리하면, 단일 프로세스 내의 간단한 동기화에는 lock, 프로세스 간 동기화에는 Mutex, 반복적인 백그라운드 작업에는 ThreadPool을 사용하는 것이 효율적입니다. 상황에 맞는 동기화 기법을 선택하면 멀티스레드 애플리케이션의 안정성과 성능을 모두 높일 수 있습니다.