본문 바로가기
Programming/C#

C# 스레드 동기화 AutoResetEvent와 ManualResetEvent의 이해와 사용법

by Dev_카페인 2024. 7. 1.
반응형

C# 스레드 동기화 AutoResetEvent와 ManualResetEvent의 이해와 사용법

AutoResetEvent와 ManualResetEvent란 무엇인가?

AutoResetEvent와 ManualResetEvent는 모두 이벤트 시그널링 메커니즘을 제공하는 클래스입니다. 이 클래스들은 스레드 간의 통신을 가능하게 하며, 특정 조건이 충족될 때까지 스레드가 기다리도록 할 수 있습니다. 이 두 클래스의 주요 차이점은 이벤트가 리셋되는 방식에 있습니다.

AutoResetEvent

AutoResetEvent는 하나의 스레드가 이벤트를 기다리다가 이벤트가 신호를 받으면, 해당 스레드는 계속 진행하고 이벤트 상태는 자동으로 리셋됩니다. 이를 통해 다른 스레드가 신호를 받을 수 있도록 합니다. 즉, 이벤트가 한 번 신호를 보내면 자동으로 리셋되어 다음 신호를 기다리게 됩니다.

ManualResetEvent

ManualResetEvent는 신호를 받으면 이벤트 상태를 리셋하지 않고 유지합니다. 이벤트 상태를 수동으로 리셋할 때까지 모든 대기 중인 스레드가 신호를 받을 수 있습니다. 이는 여러 스레드가 동일한 신호를 받아야 할 때 유용합니다.

예제 코드

AutoResetEvent 사용 예제

아래는 AutoResetEvent를 사용하는 간단한 예제입니다:

using System;
using System.Threading;

class Program
{
    static AutoResetEvent autoEvent = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        Thread worker = new Thread(Worker);
        worker.Start();

        Console.WriteLine("Main thread waiting for worker to signal...");
        autoEvent.WaitOne();
        Console.WriteLine("Main thread received signal from worker.");

        worker.Join();
    }

    static void Worker()
    {
        Console.WriteLine("Worker thread working...");
        Thread.Sleep(2000);
        Console.WriteLine("Worker thread signaling completion.");
        autoEvent.Set();
    }
}

이 예제에서 메인 스레드는 Worker 스레드가 완료될 때까지 기다립니다. Worker 스레드는 작업을 완료한 후 autoEvent.Set()을 호출하여 메인 스레드에게 신호를 보냅니다. 메인 스레드는 신호를 받은 후에 실행을 계속합니다.

ManualResetEvent 사용 예제

아래는 ManualResetEvent를 사용하는 간단한 예제입니다:

using System;
using System.Threading;

class Program
{
    static ManualResetEvent manualEvent = new ManualResetEvent(false);

    static void Main(string[] args)
    {
        Thread worker1 = new Thread(Worker);
        Thread worker2 = new Thread(Worker);
        worker1.Start();
        worker2.Start();

        Console.WriteLine("Main thread waiting for workers to signal...");
        Thread.Sleep(2000);
        manualEvent.Set();

        worker1.Join();
        worker2.Join();
    }

    static void Worker()
    {
        Console.WriteLine("Worker thread waiting for signal...");
        manualEvent.WaitOne();
        Console.WriteLine("Worker thread received signal.");
    }
}

이 예제에서 두 개의 Worker 스레드는 manualEvent.WaitOne()을 호출하여 신호를 기다립니다. 메인 스레드는 2초 후 manualEvent.Set()을 호출하여 신호를 보내고, Worker 스레드는 신호를 받고 실행을 계속합니다. ManualResetEvent는 수동으로 리셋될 때까지 모든 대기 중인 스레드에게 신호를 보냅니다.

AutoResetEvent와 ManualResetEvent의 차이점

  1. 자동 리셋 vs. 수동 리셋:
    • AutoResetEvent: 신호를 받은 후 자동으로 리셋됩니다.
    • ManualResetEvent: 신호를 받은 후 수동으로 리셋될 때까지 유지됩니다.
  2. 사용 사례:
    • AutoResetEvent: 단일 스레드가 신호를 받고, 다시 대기 상태로 돌아가는 시나리오에 적합합니다.
    • ManualResetEvent: 여러 스레드가 동일한 신호를 받아야 하는 시나리오에 적합합니다.

실전 예제: AutoResetEvent를 사용한 스레드 동기화

위의 Lock 클래스를 활용하여 AutoResetEvent를 사용한 스레드 동기화 예제를 다시 작성해보겠습니다.

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

class Lock
{
    AutoResetEvent _available = new AutoResetEvent(true);
    public void Acquire()
    {
        _available.WaitOne();
        // _available.Reset();
    }
    public void Release()
    {
        _available.Set();
    }
}

class Program
{
    static int _num = 0;
    static Lock _lock = new Lock();

    static void Thread_1()
    {
        for (int i = 0; i < 10000; i++)
        {
            _lock.Acquire();
            _num++;
            _lock.Release();
        }
    }

    static void Thread_2()
    {
        for (int i = 0; i < 10000; i++)
        {
            _lock.Acquire();
            _num--;
            _lock.Release();
        }
    }

    static void Main(string[] args)
    {
        Task t1 = new Task(Thread_1);
        Task t2 = new Task(Thread_2);
        t1.Start();
        t2.Start();

        Task.WaitAll(t1, t2);

        Console.WriteLine($"{_num} 정상적인 종료 !");
    }
}

이 예제에서 Lock 클래스는 AutoResetEvent를 사용하여 스레드가 자원을 안전하게 접근할 수 있도록 합니다.

결론

AutoResetEvent와 ManualResetEvent는 C#에서 제공하는 강력한 스레드 동기화 도구입니다. AutoResetEvent는 신호를 받은 후 자동으로 리셋되며, 단일 스레드가 신호를 기다리는 상황에 적합합니다. ManualResetEvent는 수동으로 리셋될 때까지 신호를 유지하며, 여러 스레드가 신호를 기다리는 상황에 적합합니다. 이러한 도구를 적절히 사용하여 멀티스레드 환경에서 효율적인 동기화를 구현할 수 있습니다.

반응형