포스트

스핀락(SpinLock) 구현하기 2/2

지난 포스팅에서는 스핀락화장실 앞에서 계속 기다리는 사람의 비유를 들어서 이해해보았는데요, 위키백과사전에 나와있는 정의는 다음과 같습니다.

우리가 대강 비슷하게 이해한 것 같습니다:) 이제는 이어서 spinLock클래스를 구현해봅시다. 지난 포스팅에서 문제였던 것은 바로 화장실에 동시에 두 사람이 들어가게 되어버리도록 spinLock.Acquire()를 작성했다는 것입니다. 이번에는 절대 두 사람이 동시에 들어갈 수 없도록 코드를 수정해봅시다. 이번에도 Interlocked 계열의 함수를 사용하겠습니다! 아래의 잘못된 코드를,

1
2
3
4
5
6
7
8
9
        public void Acquire()
        {
            while (_locked)
            {
                // 잠김이 풀리기를 기다린다.
            }
            // 내꺼!
            _locked = true;
        }

아래의 코드로 바꾸었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
        public void Acquire()
        {
            while (true)
            {
                // CAS Compare-And-Swap
                // 1을 넣어주기 전에 먼저 그 이전값이 0인지부터 체크한다.
                int expected = 0;
                int desired = 1;
                if (Interlocked.CompareExchange(ref _locked, desired, expected) == 0)
                    break;
            }
        }

일단 코드가 전부 while문으로 들어왔습니다. 1) 계속 문이 열렸는지(0) 체크하다가 열렸으면(0) 바로 들어가서(1) 문을 닫는다.

while문 안에서 Interlocked계열 함수를 사용했기 때문에 원자적으로 실행되는 것은 보장할 수 있게 되었습니다! 코드를 실행해보면서 글을 마치겠습니다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;

namespace ServerCore
{
    class SpinLock
    {
        volatile int _locked = 0;
        public void Acquire()
        {
            while (true)
            {
                //// _locked 에 1을 넣되, 그 이전값이 0이면 break;
                //int original = Interlocked.Exchange(ref _locked, 1);
                //if (original == 0)
                //    break;

                // CAS Compare-And-Swap
                // 1을 넣어주기 전에 먼저 그 이전값이 0인지부터 체크한다.
                int expected = 0;
                int desired = 1;
                if (Interlocked.CompareExchange(ref _locked, desired, expected) == 0)
                    break;
            }

        }

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

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

        static void Thread_1()
        {
            for (int i &#x3D; 0; i < 1000000; i++)
            {
                _lock.Acquire();
                _num++;
                _lock.Release();
            }
        }

        static void Thread_2()
        {
            for (int i &#x3D; 0; i < 1000000; i++)
            {
                _lock.Acquire();
                _num--;
                _lock.Release();
            }
        }

        static void Main(string[] args)
        {
            Task t1 &#x3D; new Task(Thread_1);
            Task t2 &#x3D; new Task(Thread_2);
            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);

            Console.WriteLine(_num);
        }
    }
}

이제 수건(?)의 개수가 안 세어져서 걱정할 일은 없겠군요!ㅎㅎㅎㅎ


이 글은 인프런의 '[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버' 강의를 참고하여 정리 목적으로 작성하였습니다. 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.