Computer >> 컴퓨터 >  >> 프로그래밍 >> C#

C# 세마포어(Semaphore) 완벽 가이드: 스레드 접근 제어 방법

C#에서 세마포어(Semaphore) 클래스는 임계 영역(Critical Section)에 동시에 접근할 수 있는 스레드의 수를 제한할 수 있도록 해줍니다. 주로 여러 스레드가 공유 자원 풀(Resource Pool)에 접근하는 것을 통제할 때 사용되며, 시스템 안정성과 데이터 무결성을 보장하는 데 중요한 역할을 합니다.

세마포어 관련 기능은 System.Threading.Semaphore 네임스페이스에 포함되어 있으며, 이 네임스페이스에는 세마포어 구현에 필요한 모든 메서드와 속성이 정의되어 있습니다.

세마포어 사용 방법

C#에서 세마포어를 사용하는 방법은 매우 간단합니다. Semaphore 객체의 인스턴스를 생성하기만 하면 됩니다. 이때 생성자는 최소 두 개의 인수를 필요로 합니다.

Semaphore 생성자 종류

다음은 MSDN 문서를 참고한 주요 생성자 목록입니다.

번호생성자 및 설명
1Semaphore(Int32, Int32)
세마포어 클래스의 새 인스턴스를 초기화합니다. 초기 진입 허용 수와 최대 동시 진입 수를 지정합니다.
2Semaphore(Int32, Int32, String)
초기 진입 허용 수와 최대 동시 진입 수를 지정하고, 선택적으로 시스템 세마포어 객체의 이름을 지정하여 새 인스턴스를 초기화합니다.
3Semaphore(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)가 단일 스레드만 허용한다면, 세마포어는 지정된 개수만큼의 스레드 동시 접근을 허용한다는 차이점을 기억해 두시면 좋습니다.