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

Yerin·2023년 9월 19일

C# 게임 서버

목록 보기
8/13

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

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

SpinLock

lock을 획득할 때까지 무한정 try를 한다.

Interlocked.Exchange

Interlocked.Exchange 키워드를 이용해서 _locked에 1이란 값을 넣어준다.
여기서 original 변수는 stack에 있는 변수이므로 경합 현상은 생각하지 않는다.

using System;
using System.Threading;

namespace SeverCore
{
    class SpinLock
    {
        volatile int _locked = 0;

        public void Acquire()
        {
            while (true)
            {
                // original <- _locked에 1을 넣어주기 이전 값
                int original = Interlocked.Exchange(ref _locked, 1);
                if (original == 0)
                    break;
            }   
        }

        public void Release()
        {
            _locked = 0;
        }
    }


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

        static void Thread_1()
        {
            for (int i = 0; i < 100000; i++)
            {
                _lock.Acquire();
                _num++;
                _lock.Release();
            }
        }
        static void Thread_2()
        {
            for (int i = 0; i < 100000; 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);
        }
    }
}

Interlocked.Exchange 부분을 자세히 보자.

int original = Interlocked.Exchange(ref _locked, 1);
if (original == 0)
	break;

즉, 위 코드는 아래와 같이 해석될 수 있다.

int original = _locked;
_locked = 1
if (original == 0)
	break;

Interlocked.CompareExchange

또는 아래와 같이 Interlocked.CompareExchange 키워드를 사용할 수도 있다.

int original =Interlocked.CompareExchange(ref _locked, 1, 0);
if (original == 0)
	break;

C++ 형식

C++ 형식으로 변환하면 아래와 같다.

int expected = 0;
int desired = 1;
if(Interlocked.CompareExchange(ref _locked, desired, expected) == expected)
	break;

Acquire()에서 처리를 해주었기 때문에 Release()는 그대로 진행해도 된다.

using System;
using System.Threading;

namespace SeverCore
{
    class SpinLock
    {
        volatile int _locked = 0;

        public void Acquire()
        {
            while (true)
            {
                int expected = 0;
                int desired = 1;
                if(Interlocked.CompareExchange(ref _locked, desired, expected) == expected)
                    break;

            }

        }

        public void Release()
        {
            _locked = 0;
        }
    }


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

        static void Thread_1()
        {
            for (int i = 0; i < 100000; i++)
            {
                _lock.Acquire();
                _num++;
                _lock.Release();
            }
        }
        static void Thread_2()
        {
            for (int i = 0; i < 100000; 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);
        }
    }
}
profile
재밌는 코딩 공부

0개의 댓글