Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

For me

[Server] ReaderWriterLock 본문

Server/Study

[Server] ReaderWriterLock

GiveZero 2023. 4. 26. 22:14

ReaderWriterLock 구현

using System;

namespace ServerCore
{
    // 재귀적 락 허용 => WriteLock -> WriteLock, WriteLock -> ReadLock (O), ReadLock -> WrtieLock (X)
    // 스핀락 정책 (5000번 -> Yield)
    public class Lock
    {
        private const int EMPTY_FLAG = 0x0000000;
        private const int WRITE_MASK = 0x7FFF0000;
        private const int READ_MASK = 0x0000FFFF;
        private const int MAX_SPIN_COUNT = 5000;

        // [Unused(1)] [WriteThreadId(15)] [ReadCount(16)]
        
        private int _flag;

        private int _writeCount = 0;
        public void WriteLock()
        {
            // 동일 스레드가 WriteLock을 이미 획득하고 있는지 확인
            int lockThreadId = (_flag & WRITE_MASK) >> 16;
            if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
            {
                _writeCount++;
                return;
            }
            
            // 아무도 WriteLock or ReadLock을 획득하고 있지 않을 때 경합해서 소유권을 얻음
            var 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;
                    }
                }
                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;
                    }

                    Thread.Yield();
                }
            }
        }

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

 

'Server > Study' 카테고리의 다른 글

[Server] Socket  (0) 2023.05.01
[Server] Thread Local Storage  (0) 2023.04.26
[Server] AutoResetEvent  (0) 2023.04.26
[Server] Context Switching  (0) 2023.04.26
[Server] Lock - 3 (SpinLock)  (0) 2023.04.26