Unity

[인프런 강의 정리] SpinLock

wny0320 2024. 9. 27. 21:48

이 글은 아래 강의를 정리한 글이다

 

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

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

www.inflearn.com

해당 정리글을 한번에 보고 싶다면 아래 링크를 참조하길 바란다

 

[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 | Notion

서버OT

mesquite-prune-8c9.notion.site


SpinLock은 앞에서 설명했던 Lock 중에 쓰레드가 락을 획득하지 못했다면 무한정으로 대기하는 종류의 락이다.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ServerCore
{
    // spinlock은 멀티 쓰레드에서 자주 나오는 면접 질문
    class SpinLock
    {
        //volatile bool _locked = false;
        volatile int _locked = 0;
        public void Acquire()
        {
            //while (_locked)
            //{
            //    // 잠금이 풀릴때까지 대기
            //}

            //// 잠금이 해제되었기 때문에 사용 가능해졌으므로 잠금을 함
            //_locked = true;
            // 이 부분을 하나로 합쳐야함

            while(true)
            {
                int original = Interlocked.Exchange(ref _locked, 1);
                if (original == 0)
                    break;
            }
            // 내가 잠금했고, 잠금하기 전 값이 0이라면(아무도 건들지 않았다면) 대기를 해제한다.
        }
        public void Release()
        {
            //_locked = false;
            _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);
            // 이론상이면 락이 풀려야 작동하기 때문에 0이어야하지만 결과는 다르다
            // 결과값 : 2632
            // 제대로 작동하지 않는다고 볼 수 있음
            // 이유는 멀티 쓰레드이기 때문에 동시에 요구했을 때 둘 다 진입이 되는 경우가 발생하기 때문

            // Interlocked 사용시에는 제대로 작동
        }
    }
}
public void Acquire()
{
    while(true)
    {
        int expected = 0; // 예상값
        int desired = 1; // 요구값
        // C++ 방식으로 좀 더 읽기 명확하게 하기 위한 변수 할당
        if (Interlocked.CompareExchange(ref _locked, desired, expected) == expected)
            break;
        // 이 방식이 좀 더 일반적인 방식
        // CAS Compare-And-Swap 방식, 언어마다 동작 방식이 달라서 보고 맞춰줘야 함
    }
}

진입을 혼자 했는지 확인 후 작업하기 때문에 Release, 즉 잠금 해제는 Interlocked 계열이 아닌 그냥 변수로 해도 상관 없음