Lock ๊ตฌํ˜„์—๋Š” ์—ฌ๋Ÿฌ ๊ตฌํ˜„๋ฐฉ์‹์ด์žˆ๋Š”๋ฐ ๊ทธ์ค‘์— SpinLock๊ณ„์—ด์„ ์„ค๋ช…ํ•˜๋ ค๊ณ ํ•ฉ๋‹ˆ๋‹ค


C# SpinLock ๊ตฌํ˜„๊ณผ ๋™์ž‘ ์›๋ฆฌ

1. SpinLock ๊ฐœ๋…

SpinLock์€ ์ž ๊ธˆ์ด ํ’€๋ฆด ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณต์ ์œผ๋กœ ํ™•์ธ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™๊ธฐํ™”๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” Lock ๊ธฐ๋ฒ•์ด๋‹ค.
์ž ๊ธˆ ํ•ด์ œ ์ „๊นŒ์ง€ CPU๋ฅผ ๊ณ„์† ์‚ฌ์šฉํ•˜๋ฉฐ ๋ฃจํ”„๋ฅผ ๋„๋Š” ๋ฐฉ์‹์ด๋ผ, ์งง์€ ์ž ๊ธˆ ์œ ์ง€ ์‹œ๊ฐ„์—๋Š” ํšจ์œจ์ ์ด์ง€๋งŒ, ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋ฉด ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.


2. ์ฝ”๋“œ ๊ตฌํ˜„ (์ฃผ์„ ํฌํ•จ)

using System;
using System.Threading;
using System.Threading.Tasks;
 
 
namespace ServerCore
{
    class SpinLock
    {
        // ์ƒํƒœ
        // true - ์ž ๊น€ / false - ์ž ๊ธฐ์ง€ ์•Š์Œ
        // volatile: ๊ฐ€์‹œ์„ฑ ๋ณด์žฅ (๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ๋ณ€๊ฒฝ๋œ ๊ฐ’ ๋ฐ”๋กœ ๋ณด์ด๊ฒŒ)
        // volatile bool isLocked = false;
 
        volatile int isLocked = 0; // int๋กœ ํ•˜๋ฉด Interlocked ๊ณ„์—ด ํ•จ์ˆ˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
 
        // ์ž ๊ธˆ ํš๋“
        public void Acquire()
        {
            // ์˜›๋‚  ๋ฐฉ์‹
            // while (isLocked) { }  // ๋บ‘๋บ‘์ด ๋Œ๋ฉฐ ๊ธฐ๋‹ค๋ฆผ
            // isLocked = true;
 
            // ๋ฌธ์ œ์ : while ๋๋‚˜๊ณ  isLocked = true ์‚ฌ์ด์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ผ์–ด๋“ค ์ˆ˜
            ์žˆ์Œ โ†’ ์›์ž์  ์ฒ˜๋ฆฌ ํ•„์š”
 
            // --------------------------------------------------------------
            // ํ•ด๊ฒฐ์ฑ… 1: Interlocked.Exchange
            // --------------------------------------------------------------
            // int original = Interlocked.Exchange(ref isLocked, 1);
            // if (original == 0) โ†’ ์•„๋ฌด๋„ ์ ์œ  ์•ˆ ํ•จ, ๋‚ด๊ฐ€ ๊ฐ€์ ธ๊ฐ
 
            // --------------------------------------------------------------
            // ํ•ด๊ฒฐ์ฑ… 2: Interlocked.CompareExchange (CAS, Compare-And-Swap)
            // --------------------------------------------------------------
            while (true)
            {
                int expected = 0; // ๋‚ด๊ฐ€ ์˜ˆ์ƒํ•œ ๊ฐ’ (์ž ๊ธˆ ์•ˆ ๊ฑธ๋ฆฐ ์ƒํƒœ)
                int desired = 1;  // ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๊ฐ’ (์ž ๊ธˆ ์ƒํƒœ)
 
                // CompareExchange:
                //  isLocked == expected โ†’ isLocked = desired ๋กœ ๋ฐ”๊พธ๊ณ  ๊ธฐ์กด
                ๊ฐ’ ๋ฐ˜ํ™˜
                //  ์•„๋‹ˆ๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ•˜๊ณ  ๊ธฐ์กด ๊ฐ’ ๋ฐ˜ํ™˜
                if (expected == Interlocked.CompareExchange(ref isLocked, desired, expected))
                    break; // ์„ฑ๊ณตํ•˜๋ฉด ๋‚˜๊ฐ€๊ธฐ
            }
        }
 
        // ์ž ๊ธˆ ํ•ด์ œ
        public void Release()
        {
            // ๋‚ด๊ฐ€ ์ด๋ฏธ ์ž์›์„ ์ ์œ ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ๊ทธ๋ƒฅ 0์œผ๋กœ ๋ฐ”๊ฟ”๋„ ๋จ
            isLocked = 0;
        }
    }
 
    class Program
    {
        static int num = 0;
        static SpinLock Lock = new SpinLock();
 
        static void Thread_1()
        {
            for (int i = 0; i < 1000000; ++i)
            {
                Lock.Acquire();
                num++;
                Lock.Release();
            }
        }
 
        static void Thread_2()
        {
            for (int i = 0; i < 1000000; ++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);
        }
    }
}

3. ํ•ต์‹ฌ ๋™์ž‘ ์›๋ฆฌ

3.1 Interlocked.Exchange

  • ๊ฐ’ ๊ตํ™˜ (Swap) ์„ ์›์ž์ ์œผ๋กœ ์ˆ˜ํ–‰.

  • ๊ธฐ์กด ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ˜ํ™˜๊ฐ’์ด 0์ด๋ฉด ๋ฝ์ด ๋น„์–ด์žˆ๋Š” ์ƒํƒœ.

int original = Interlocked.Exchange(ref isLocked, 1);
if (original == 0)
{
    // ๋ฝ์„ ํš๋“ํ•จ
}

3.2 Interlocked.CompareExchange

  • CAS (Compare-And-Swap) ๊ธฐ๋ฒ•.

  • isLocked ๊ฐ’์ด ์˜ˆ์ƒ ๊ฐ’(expected)์ด๋ฉด ์ƒˆ ๊ฐ’(desired)๋กœ ๋ฐ”๊พธ๊ณ , ๊ทธ ์ „ ๊ฐ’์„ ๋ฐ˜ํ™˜.

  • ๊ธฐ๋Œ€ํ•œ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋ฉด ์„ฑ๊ณต, ์•„๋‹ˆ๋ฉด ์‹คํŒจ.

  • CompareExchange ๋ฐ˜ํ™˜ ๊ฐ’์€ isLocked์˜ desired ์‚ฝ์ž… ์ „ ๊ฐ’์ด๋‹ค

  • ๊ฒฐ๊ตญ expected๋ผ๋ฉด ์•„๋ฌด๋„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•˜๋‹ค๋Š” ๋กœ์ง

int expected = 0;
int desired = 1;
 
if (expected == Interlocked.CompareExchange(ref isLocked, desired, expected))
{
    // ๋ฝ ํš๋“ ์„ฑ๊ณต
}

4. ์‚ฌ์šฉ ์˜ˆ์‹œ

class Program
{
    static int num = 0;
    static SpinLock Lock = new SpinLock();
 
    static void Thread_1()
    {
        for (int i = 0; i < 1000000; ++i)
        {
            Lock.Acquire();
            num++;
            Lock.Release();
        }
    }
 
    static void Thread_2()
    {
        for (int i = 0; i < 1000000; ++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 ์ถœ๋ ฅ
    }
}`

5. Spinlock Yield

  • ์ž์› ์ ์œ ๋ฅผ ์œ„ํ•ด ์ง€์†์ ์œผ๋กœ ๊ธฐ๋‹ค๋ฆฌ๋Š” SpinLock๊ณผ ๋‹ฌ๋ฆฌ ์–‘๋ณด๋ฅผ ํ•˜๊ฑฐ๋‚˜ ์ž ์‹œ ์‰ฌ์—ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ์ž์›์ ์œ ๋ฅผ ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค

  • ์žฅ์  : ๊ฒฝํ•ฉ๋ถ€๋ถ„์— ์ž‘์—…ํ•˜๋Š” ์–‘์ด๋งŽ๋‹ค๋ฉด ๋‹ค๋ฅธ์Šค๋ ˆ๋“œ๊ฐ€ ์ง€์†์ ์œผ๋กœ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ์ค„์—ฌ์คŒ์œผ๋กœ ๋‹ค๋ฅธ ์ž‘์—…์„ ํ•˜๋Š” ์Šค๋ ˆ๋“œ์˜ ์ผ์„ ์‹œํ‚ฌ์ˆ˜์žˆ๋‹ค

  • ๋‹จ์  : ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ž‘์—…์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ์ƒ๋˜๋ฉด ์˜ค๋ฒ„ํ—ค๋“œ ๋ฐœ์ƒ๋Ÿ‰์ด ๋†’์•„์ง์œผ๋กœ ์‹œ๊ฐ„์ ์ž์› ์‚ฌ์šฉ๋Ÿ‰์ด ์ปค์ง

  class SpinLock
  {
      volatile int isLocked = 0;

      //์ž ๊ธˆ
      public void Acquire()
      {
          while (true)
          {
              int expected = 0; //๋‚ด๊ฐ€ ์˜ˆ์ƒํ•œ๊ฐ’
              int desired = 1; //๋‚ด๊ฐ€ ์›ํ•˜๋Š”๊ฐ’

              if (expected == Interlocked.CompareExchange(ref isLocked, desired, expected)) 
                  break;
              
              //์‰ฌ๋‹ค ์˜ค๋Š”๊ฒƒ
              //์‰ฌ๋‹ค์˜ค๋Š” ๋ฐฉ๋ฒ•์ด ์„ธ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค

              //Sleep Ms๋ฐ›๋Š”๊ฒƒ 
              Thread.Sleep(1); //1 ms๋งŒํผ ๋Œ€๊ธฐํ•˜๊ฒ ๋‹ค ๋ฌด์กฐ๊ป€ ํœด์‹ -> ๋ช…์‹์ƒ์ด๋ ‡๊ฒŒ๋˜์–ด์žˆ์ง€๋งŒ ์Šค์ผˆ์ค„๋Ÿฌ์—์˜ํ•ด ๋” ๊ธด์‹œ๊ฐ„์ด ๋ ์ˆ˜๋„์žˆ๋‹ค

              Thread.Sleep(0); // ์กฐ๊ฑด๋ถ€ ์–‘๋ณด -> ๋‚˜๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ์€ ์• ๋“คํ•œํ…Œ๋Š” ์–‘๋ณด ๋ถˆ๊ฐ€ -> ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚˜๋ณด๋‹ค ๊ฐ™๊ฑฐ๋‚˜ ๋†’์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์—†์œผ๋ฉด ๋‹ค์‹œ ๋ณธ์ธํ•œํ…Œ์˜จ๋‹ค
                               // ์šฐ์„ ์ˆœ์œ„๋ž€ : ์ง์›๋งˆ๋‹ค ๊ณตํ‰ํ•˜๊ฒŒ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๊ฐ™์„์ˆ˜๋„์žˆ์ง€๋งŒ ์•„๋‹์ˆ˜๋„์žˆ๋‹ค
                               // ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ๋‹ค๋ฉด ๊ธฐ์•„ํ˜•์ƒ๋ฐœ์ƒ

              Thread.Yield();  // ๊ด€๋Œ€ํ•œ ์–‘๋ณด : ๊ด€๋Œ€ํ•˜๊ฒŒ ์–‘๋ณดํ• ํ…Œ๋‹ˆ, ์ง€๊ธˆ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•œ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์‹คํ–‰ํ•˜์„ธ์š” 
                               // ์‹คํ–‰๊ฐ€๋Šฅํ•œ ์• ๊ฐ€ ์—†์œผ๋ฉด ๋ณธ์ธํ•œํ…Œ ๋‚จ์€์‹œ๊ฐ„ ์†Œ์ง„

              //์žฅ์  : ๋ฌดํ•œ์ •์œผ๋กœ ๋บ‘๋บ‘๋„๋Š”๊ฒƒ์„ ๋ฐฉ์ง€
              //๋‹จ์  : ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์œผ๋กœ์ธํ•œ ์˜ค๋ฒ„ํ—ค๋“œ ๋ฐœ์ƒ, ์†Œ์œ ๊ถŒ์„ ๋บ๊ธธ์ˆ˜๋„์žˆ๋‹ค 

              //์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ : ํ˜„์žฌ ์Šค๋ ˆ๋“œ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋‹ค์Œ์‹คํ–‰ํ•  ์Šค๋ ˆ๋“œ ์ƒํƒœ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ๋™์ž‘์‹œํ‚ค๋Š”๊ฒƒ

              //์ˆœ์„œ
              //1. ํ˜„์žฌ ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ €์žฅ
              //  ->๋ ˆ์ง€์Šคํ„ฐ ๊ฐ’, ํ”„๋กœ๊ทธ๋žจ ์นด์šดํ„ฐ(PC), ์Šคํƒ ํฌ์ธํ„ฐ(SP), ๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘ ์ •๋ณด ๋“ฑ.

              //2. ๋‹ค์Œ ์‹คํ–‰ํ•  ์Šค๋ ˆ๋“œ ์ƒํƒœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
              //  -> ์ €์žฅ๋ผ ์žˆ๋˜ ๋ ˆ์ง€์Šคํ„ฐ, PC, SP ๋ณต์›.

              //3. ์ƒˆ ์Šค๋ ˆ๋“œ ์‹คํ–‰ ์‹œ์ž‘
          }
      }

      //๋ฐ˜ํ™˜
      public void Release()
      {
          isLocked = 0;
      }
  }

6. Lock ๊ตฌํ˜„ ๋ฐฉ์‹ ๋น„๊ต

๋ฐฉ์‹์„ค๋ช…์žฅ์ ๋‹จ์ 
SpinLock์ž ๊ธˆ์ด ํ’€๋ฆด ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋ฃจํ”„์งง์€ ์ž‘์—…์— ํšจ์œจ์ , ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ ์—†์Œ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋ฉด CPU ์ ์œ ์œจ ๊ธ‰์ฆ
Yield์ž ๊ธˆ ์‹คํŒจ ์‹œ CPU ์ ์œ ๊ถŒ ๋ฐ˜๋‚ฉCPU ๋‚ญ๋น„ ์ค„์ž„์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ ์˜ค๋ฒ„ํ—ค๋“œ
EventOS์— ์ด๋ฒคํŠธ ๋“ฑ๋ก ํ›„ ์‹ ํ˜ธ ๋ฐ›์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐCPU ๋‚ญ๋น„ ๊ฑฐ์˜ ์—†์Œ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋น„์šฉ, ๊ตฌํ˜„ ๋ณต์žก

๐Ÿ’ก ์ •๋ฆฌ:
SpinLock์€ ์Šค๋ ˆ๋“œ๋‚˜ ํ”„๋กœ์„ธ์Šค์˜ ๊ฒฝํ•ฉ ์ƒํ™ฉ์ด ์งง์„๋•Œ ์œ ์šฉํ•˜๋‹ค ์™œ? ContextSwitching์—†์ด ๋ฝ์˜ ํš๋“๊ณผ ๋ฐ˜ํ™˜์ด ๋น ๋ฅด๊ฒŒ ์ด์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค ๋ฐ˜๋Œ€๋กœ ๋งํ•˜๋ฉด ๊ฒฝํ•ฉ์ƒํ™ฉ์ด ๊ธธ์–ด์ง„๋‹ค๋ฉด ๋ฝ์˜ ํš๋“ํ•˜๋Š” ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€๊ธฐ์— cpu์ ์œ ์œจ์ƒ์Šน