본문 바로가기
C#/쓰레드

C# 스레드 제어: Semaphore와 SemaphoreSlim의 차이와 사용법

by 공부봇 2025. 1. 16.
반응형

스레드 제어에서 세마포어(Semaphore) 개념

세마포어는 멀티스레드 프로그래밍에서 리소스에 대한 접근을 제어하기 위한 도구입니다. 한정된 리소스(예: 데이터베이스 연결, 파일 처리 등)에 대해 동시에 접근 가능한 스레드 수를 제한하기 위해 사용됩니다.


세마포어(Semaphore)의 기본 개념

  1. 카운터 역할:
    • 세마포어는 내부적으로 카운터를 가지고 있습니다.
    • 이 카운터는 특정 리소스에 접근 가능한 최대 스레드 수를 나타냅니다.
    • 예를 들어, 카운터가 3이면, 동시에 3개의 스레드만 리소스에 접근할 수 있습니다.
  2. 동작 원리:
    • 스레드가 세마포어에 접근하면 카운터가 감소합니다.
    • 작업이 끝나면 카운터가 증가합니다.
    • 카운터가 0이면, 다른 스레드는 대기 상태가 됩니다.
  3. 비유:
    • 세마포어를 주차장으로 생각할 수 있습니다.
    • 주차장은 주차 가능한 최대 차량 수(카운터)를 설정합니다.
    • 차량(스레드)이 들어오면 빈 자리가 줄어들고, 나가면 빈 자리가 늘어납니다.
    • 빈 자리가 없으면(카운터가 0) 차량은 기다려야 합니다.

C#에서의 세마포어

1. Semaphore

C#에서 Semaphore는 .NET의 기본 제공 클래스입니다.

  • 생성:
    • initialCount: 현재 사용할 수 있는 리소스 개수.
    • maximumCount: 최대 허용 리소스 개수.
Semaphore semaphore = new Semaphore(initialCount: 3, maximumCount: 3);
  • 사용:
semaphore.WaitOne(); // 세마포어를 잠금(카운터 감소)

// 임계 구역 (리소스 사용 코드)

semaphore.Release(); // 세마포어 잠금 해제(카운터 증가)
  • 특징:
    • 프로세스 간 공유 가능: 네임드 세마포어를 사용하면 여러 프로세스에서 세마포어를 공유할 수 있습니다.
    • 리소스가 다른 프로세스와 공유되어야 하는 경우 유리합니다.

2. SemaphoreSlim

SemaphoreSlim은 .NET에서 제공하는 경량화된 세마포어입니다.

  • 생성:
    • initialCount와 maxCount는 Semaphore와 동일하게 설정합니다.
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(initialCount: 3, maxCount: 3);
  • 사용:
await semaphoreSlim.WaitAsync(); // 비동기적으로 세마포어 잠금

// 임계 구역 (리소스 사용 코드)

semaphoreSlim.Release(); // 세마포어 잠금 해제
  • 특징:
    • 프로세스 내 사용에 최적화되어 있습니다.
    • 비동기 지원(WaitAsync)을 기본적으로 제공하므로 비동기 작업에 적합합니다.
    • 시스템 리소스를 적게 사용하므로 성능이 더 좋습니다.

Semaphore와 SemaphoreSlim의 차이점

특징 Semaphore SemaphoreSlim

리소스 공유 프로세스 간 공유 가능 프로세스 내에서만 사용 가능
비동기 지원 기본적으로 비동기 지원 없음 WaitAsync로 비동기 지원
성능 상대적으로 무거움 경량화, 성능 우수
사용 시나리오 프로세스 간 리소스 동기화 필요 프로세스 내부 동기화

어떤 것을 언제 사용할까?

  1. Semaphore를 사용할 때:
    • 프로세스 간 리소스를 공유하고자 할 때.
    • 예: 두 개 이상의 애플리케이션에서 동일한 파일에 접근해야 하는 경우.
  2. SemaphoreSlim을 사용할 때:
    • 단일 프로세스 내 동기화가 필요할 때.
    • 비동기 작업을 사용할 때 (Task 기반의 비동기 프로그래밍).
    • 시스템 리소스를 최적화하고 싶을 때.

예제 코드: SemaphoreSlim

아래는 SemaphoreSlim을 사용하여 동시에 3개의 스레드만 실행되도록 제한하는 예제입니다.

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(3); // 최대 3개의 스레드 허용

    static async Task Worker(int id)
    {
        Console.WriteLine($"Worker {id} 대기 중...");
        await semaphoreSlim.WaitAsync(); // 세마포어 잠금
        
        try
        {
            Console.WriteLine($"Worker {id} 시작");
            await Task.Delay(2000); // 작업 시뮬레이션
            Console.WriteLine($"Worker {id} 완료");
        }
        finally
        {
            semaphoreSlim.Release(); // 세마포어 해제
            Console.WriteLine($"Worker {id} 종료 후 세마포어 해제");
        }
    }

    static async Task Main(string[] args)
    {
        Task[] tasks = new Task[10];
        for (int i = 0; i < 10; i++)
        {
            tasks[i] = Worker(i);
        }

        await Task.WhenAll(tasks);
        Console.WriteLine("모든 작업 완료!");
    }
}

요약

  1. Semaphore: 프로세스 간 리소스 동기화.
  2. SemaphoreSlim: 프로세스 내부의 경량화된 동기화, 비동기 작업에 적합.
  3. 선택 기준:
    • 다른 프로세스와 리소스를 공유해야 한다면 Semaphore.
    • 단일 프로세스 내에서 비동기 작업을 포함한 동기화가 필요하다면 SemaphoreSlim.

추가적인 질문이 있으면 언제든 물어보세요! 😊

 

 

 

 

반응형