C#에서 세마포어(Semaphore) 클래스는 임계 영역(Critical Section)에 동시에 접근할 수 있는 스레드의 수를 제한할 수 있도록 해줍니다. 주로 여러 스레드가 공유 자원 풀(Resource Pool)에 접근하는 것을 통제할 때 사용되며, 시스템 안정성과 데이터 무결성을 보장하는 데 중요한 역할을 합니다.
세마포어 관련 기능은 System.Threading.Semaphore 네임스페이스에 포함되어 있으며, 이 네임스페이스에는 세마포어 구현에 필요한 모든 메서드와 속성이 정의되어 있습니다.
세마포어 사용 방법
C#에서 세마포어를 사용하는 방법은 매우 간단합니다. Semaphore 객체의 인스턴스를 생성하기만 하면 됩니다. 이때 생성자는 최소 두 개의 인수를 필요로 합니다.
Semaphore 생성자 종류
다음은 MSDN 문서를 참고한 주요 생성자 목록입니다.
| 번호 | 생성자 및 설명 |
|---|---|
| 1 | Semaphore(Int32, Int32) 세마포어 클래스의 새 인스턴스를 초기화합니다. 초기 진입 허용 수와 최대 동시 진입 수를 지정합니다. |
| 2 | Semaphore(Int32, Int32, String) 초기 진입 허용 수와 최대 동시 진입 수를 지정하고, 선택적으로 시스템 세마포어 객체의 이름을 지정하여 새 인스턴스를 초기화합니다. |
| 3 | Semaphore(Int32, Int32, String, Boolean) 초기 진입 허용 수와 최대 동시 진입 수를 지정하고, 선택적으로 시스템 세마포어 객체의 이름을 지정하며, 새로운 시스템 세마포어가 생성되었는지 여부를 나타내는 값을 받는 변수를 지정하여 인스턴스를 초기화합니다. |
이제 실제 예제를 살펴보겠습니다.
아래 예제에서는 최대 동시 진입 수를 지정하고 일부 진입을 예약하는 세마포어 생성자를 사용했습니다.
static Semaphore semaphore = new Semaphore(2, 2);
여기서 첫 번째 매개변수는 초기 진입 허용 수, 두 번째 매개변수는 최대 동시 진입 수를 의미합니다. 즉, 위 코드는 동시에 최대 2개의 스레드만 임계 영역에 진입할 수 있도록 설정한 것입니다.
예제 코드
using System;
using System.Threading;
namespace Program
{
class Demo
{
static Thread[] t = new Thread[5];
static Semaphore semaphore = new Semaphore(2, 2);
static void DoSomething()
{
Console.WriteLine("{0} = waiting", Thread.CurrentThread.Name);
semaphore.WaitOne();
Console.WriteLine("{0} begins!", Thread.CurrentThread.Name);
Thread.Sleep(1000);
Console.WriteLine("{0} releasing...", Thread.CurrentThread.Name);
semaphore.Release();
}
static void Main(string[] args)
{
for (int j = 0; j < 5; j++)
{
t[j] = new Thread(DoSomething);
t[j].Name = "thread number " + j;
t[j].Start();
}
Console.Read();
}
}
}코드 설명
- WaitOne(): 세마포어에 진입 요청을 합니다. 이미 최대 스레드 수가 진입 중이라면 대기 상태가 됩니다.
- Release(): 작업이 끝난 후 세마포어를 해제하여 다른 대기 중인 스레드가 진입할 수 있도록 합니다.
- Thread.Sleep(1000): 작업 시간을 시뮬레이션하기 위해 1초간 대기합니다.
실행 결과
위 코드를 실행하면 다음과 같은 출력 결과를 확인할 수 있습니다.
thread number 2 = waiting thread number 0 = waiting thread number 3 = waiting thread number 1 = waiting thread number 4 = waiting thread number 2 begins! thread number 1 begins! thread number 2 releasing... thread number 1 releasing... thread number 4 begins! thread number 3 begins! thread number 4 releasing... thread number 0 begins! thread number 3 releasing... thread number 0 releasing...
출력 결과를 보면 5개의 스레드가 모두 대기 상태로 진입했지만, 세마포어가 동시 진입을 2개로 제한했기 때문에 처음에는 2개의 스레드만 실행되고, 나머지 스레드는 앞선 스레드가 Release()를 호출한 후 순차적으로 실행되는 것을 알 수 있습니다.
정리
세마포어는 멀티스레딩 환경에서 동시 접근 가능한 스레드 수를 제어하는 강력한 도구입니다. 특히 데이터베이스 연결 풀, 파일 접근 제한 등 유한한 리소스를 관리해야 하는 상황에서 유용하게 활용할 수 있습니다. 뮤텍스(Mutex)가 단일 스레드만 허용한다면, 세마포어는 지정된 개수만큼의 스레드 동시 접근을 허용한다는 차이점을 기억해 두시면 좋습니다.