[C# 서버] Lock 구현 (AutoResetEvent)

Yerin·2023년 9월 19일

C# 게임 서버

목록 보기
10/13

Lock을 누가 점유하고 있다면?

1) 무작정 기다린다. => SpinLock
2) 나중에 다시 시도한다. (랜덤성) => 쓰레드가 자기 소유권을 포기 Context Switching
3) 운영체제의 도움을 받는다. => AutoResetEvent cf) Manual Reset Event

AutoResetEvent (자동)

using System;
using System.Threading;

namespace SeverCore
{
    class Lock
    {
        //bool <- 커널
        AutoResetEvent _available = new AutoResetEvent(true); //문이 열린 상태

        public void Acquire()
        {
            _available.WaitOne(); //입장 시도
            //_available.Reset(); //위 코드에 포함
        }

        public void Release()
        {
            _available.Set(); //flag = true
        }
    }


    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);
        }
    }
}

하지만 SpinLock 방식보다는 시간이 오래 걸린다.

ManualResetEvent (수동)

using System;
using System.Threading;

namespace SeverCore
{
    class Lock
    {
        //bool <- 커널
        ManualResetEvent _available = new ManualResetEvent(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);
        }
    }
}

why?) 결과가 1이 나오는 이유는?

문을 열고 닫는 과정이 2개로 나뉘어져 있다.
즉 AutoResetEvent를 이용해 아래 과정을 하나로 묶었어야 한다.

_available.WaitOne(); //입장 시도
_available.Reset(); //문을 닫는다.

그렇다면 ManualResetEvent는 언제 사용할 것인가?

=> 한 번에 하나씩 입장하는 경우가 아니라
어떤 작업이 끝났으면 모든 쓰레드들이 다시 재가동 할 수 있게 할 때 사용할 수 있다.

Mutex

AutoResetEvent와 같은 동작을 한다고 생각할 수 있다.
AutoResetEvent는 bool 값을 이용하지만 Mutex는 int를 이용할 수 있기 때문에
몇번 lock 했는지도 count 할 수 있다.
또한 ThreadId를 가지기에 어떤 부분에서 잘못됐는지 파악할 수 있다.
다만 추가 비용이 발생한다.
AutoResetEvent와 Mutex 모두 커널단에서 작동하기에 속도가 느리다.

using System;
using System.Threading;

namespace SeverCore
{
    class Program
    {
        static int _num = 0;
        static Mutex _lock = new Mutex();

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


        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);
        }
    }
}
profile
재밌는 코딩 공부

0개의 댓글