C#/C# 서버

[C#/서버] ReaderWriterLock

진서박 2023. 8. 22. 12:15
반응형

인프런 - Rookies님의 강의

https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4

 

[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 인프런 | 강의

네트워크/멀티쓰레드/운영체제 등 핵심 전공 지식을 공부하고 게임 서버를 바닥부터 만들어보면서 MMORPG 기술을 학습하는 강의입니다., MMORPG 개발에 필요한 모든 기술, C# + Unity로 Step By Step! 🕹

www.inflearn.com

 

 

 

    // 재귀적 락을 허용할지 (No)
    // 재귀적 락을 허용할지 (Yes) : WriteLock 상태에서 -> WriteLock 잡는거 OK /  WriteLock 상태에서 -> ReadLock 잡는거 OK / ReadLock 상태에서 WriteLock NO
    // 스핀락 정책 (5000번 -> Yield)
    internal class Lock
    {
        // [Unused(1)] [WriteThreadId(15비트)] [ReadCount(16비트)]
        // => int(32비트)
        // Unused(1비트) : 32비트 중에 마지막 값은 음수가 될 수 있기 때문에 사용 안함 
        // 15비트를 WriteThreadId로 사용 / 16비트를 ReadCount로 사용 => 총 31비트만 사용
        // => WRITE_MASK를 사용할 때 15비트 / READ_MASK를 사용할 때 16비트
        // => ReadCount만 추출하고 싶을 때, READ_MASK를 씌워서 앞의 15비트는 0으로 밀어버리고 추출
        // => WriteCount만 추출하고 싶을 때, WRITE_MASK를 씌워서 뒤의 16비트는 0으로 밀어버리고 추출

        const int EMPTY_FLAG = 0x00000000;
        const int WRITE_MASK = 0x7FFF0000;
        const int READ_MASK = 0x0000FFF;
        const int MAX_SPIN_COUNT = 5000;

        int _flag = EMPTY_FLAG;
        int _writeCount = 0;

        public void WriteLock()
        {
            // 동일 쓰레드가 WriteLock을 이미 획득하고 있는지 확인
            int lockThreadId = (_flag & WRITE_MASK) >> 16;
            if(Thread.CurrentThread.ManagedThreadId == lockThreadId)
            {
                _writeCount++;
                return;
            }

            // 아무도 WriteLock or ReadLock을 획득하고 있지 않을 때, 경합해서 소유권을 얻는다
            // ManagedThreadId : 1부터 쭉 늘어나는 숫자
            // 16비트 만큼 밀어줌
            // & WRITE_MASK : WriteThreadId를 제외한 나머지 부분은 0으로 밀어줌

            int desired = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK;
            while (true)
            {
                for (int i = 0; i < MAX_SPIN_COUNT; i++)
                {
                    // 시도를 해서 성공하면 return
                    if (Interlocked.CompareExchange(ref _flag, desired, EMPTY_FLAG) == EMPTY_FLAG)
                    {
                        _writeCount = 1;
                        return;
                    }

                    // ==
                    // if (_flag == EMPTY_FLAG)
                    //     _flag = desired;
                }

                Thread.Yield();
            }

        }

        public void WriteUnlock()
        {
            int lockCount = --_writeCount;
            if(lockCount == 0)
                Interlocked.Exchange(ref _flag, EMPTY_FLAG);
        }

        public void ReadLock()
        {
            // 동일 쓰레드가 WriteLock을 이미 획득하고 있는지 확인
            int lockThreadId = (_flag & WRITE_MASK) >> 16;
            if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
            {
                Interlocked.Increment(ref _flag);
                return;
            }

            // 아무도 WriteLock을 획득하고 있지 않으면, ReadCount를 1 늘린다
            while (true)
            {
                for(int i = 0; i < MAX_SPIN_COUNT; i++)
                {
                    int expected = (_flag & READ_MASK);
                    if (Interlocked.CompareExchange(ref _flag, expected + 1, expected) == expected)
                        return;

                    // == 
                    //if ((_flag & WRITE_MASK) == 0)
                    //{
                    //    _flag = _flag + 1;
                    //    return;
                    //}
                }
                Thread.Yield();
            }
        }

        public void ReadUnlock()
        {
            Interlocked.Decrement(ref _flag);
        }
    }
반응형

'C# > C# 서버' 카테고리의 다른 글

[C#/서버] 기초 소켓 프로그래밍  (0) 2023.08.22
[C#/서버] AutoResetEvent / ManualResetEvent  (0) 2023.08.20
[C#/서버] SpinLock  (0) 2023.08.18
[C#/서버] 메모리 배리어  (0) 2023.08.16
[C#/서버] 캐시 이론  (0) 2023.08.16