[Thread] ManualResetEvent / AutoResetEvent

16762 단어 CSThreadCS

스핀 락과 같이 무한정으로 기다리는 스레드 대기 방식은 CPU 점유율이 올라가는 부담이 크다. 이럴 경우 스레드 대기가 끝나는 순간을 제 3자인 커널이 알려준다면 이러한 부담은 줄일 수 있을 것이다. ( 하지만 이 경우 커널 영역으로 요청을 보내기에 성능 소모가 크다)
이러한 방법은 C#에서 대표적으로 두 가지 클래스가 존재한다

🚀 ManualResetEvent

 class Lock
    {
        // 생성자의 true : 락 진입 가능 여부
        private ManualResetEvent mre = new ManualResetEvent(true);

        public void Enter()
        {
            mre.WaitOne(); // 락 진입 시도 및 대기
            mre.Reset();
        }

        public void Exit()
        {
            mre.Set(); // 락 해제
        }
    }
    class Program
    {
        private static readonly Lock _lock = new Lock();

        private const int Count = 100000;
        private static int number = 0;

        private static void ThreadBody1()
        {
            for (int i = 0; i < Count; i++)
            {
                _lock.Enter();
                number++;
                _lock.Exit();
            }
        }

        private static void ThreadBody2()
        {
            for (int i = 0; i < Count; i++)
            {
                _lock.Enter();
                number--;
                _lock.Exit();
            }
        }

        static void Main(string[] args)
        {
            Task t1 = new Task(ThreadBody1);
            Task t2 = new Task(ThreadBody2);

            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);
            Console.WriteLine(number);
        }

    }
  • ManualResetEvent의 WaitOne() 함수는 현재 lock을 가지고 있는 스레드가 없다면 임계 영역에 진입을 하게 된다.

  • Reset() 함수는 락을 설정하고 다른 스레드가 임계 구역에 못 들어오게 한다

  • Set() 함수는 락을 해제한다

  • 위 코드의 경우 0이 출력된다고 예상하는 것이 일반적이겠지만,

  • ManualResetEvent는 임계 구역에 진입할지 확인하는(데이터를 읽는) WaitOne()과 락을 설정하는(데이터를 쓰는) Reset()이 원자성을 보장하지 못하기에 다른 값이 출력되는 것을 볼 수 있다

🚀 AutoResetEvent

class Lock
    {
        // 생성자의 true : 락 진입 가능 여부
        private AutoResetEvent are = new AutoResetEvent(true);

        public void Enter()
        {
            are.WaitOne(); // 락 진입 시도 및 대기
        }

        public void Exit()
        {
            are.Set(); // 락 해제
        }
    }
    class Program
    {
        private static readonly Lock _lock = new Lock();

        private const int Count = 100000;
        private static int number = 0;

        private static void ThreadBody1()
        {
            for (int i = 0; i < Count; i++)
            {
                _lock.Enter();
                number++;
                _lock.Exit();
            }
        }

        private static void ThreadBody2()
        {
            for (int i = 0; i < Count; i++)
            {
                _lock.Enter();
                number--;
                _lock.Exit();
            }
        }

        static void Main(string[] args)
        {
            Task t1 = new Task(ThreadBody1);
            Task t2 = new Task(ThreadBody2);

            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);
            Console.WriteLine(number);
        }

    }
  • AutoResetEvent는 ManualResetEvent와 다르게 WaitOne()에 Reset(락을 설정하는)기능이 함께 있기에 원자성이 보장되며 ,

  • 예상했던 값이 출력되는 것을 확인할 수 있다

좋은 웹페이지 즐겨찾기