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 ๋ญ๋น ์ค์ | ์ปจํ ์คํธ ์ค์์นญ ์ค๋ฒํค๋ |
| Event | OS์ ์ด๋ฒคํธ ๋ฑ๋ก ํ ์ ํธ ๋ฐ์ ๋๊น์ง ๋๊ธฐ | CPU ๋ญ๋น ๊ฑฐ์ ์์ | ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋น์ฉ, ๊ตฌํ ๋ณต์ก |
๐ก ์ ๋ฆฌ:
SpinLock์ ์ค๋ ๋๋ ํ๋ก์ธ์ค์ ๊ฒฝํฉ ์ํฉ์ด ์งง์๋ ์ ์ฉํ๋ค
์? ContextSwitching์์ด ๋ฝ์ ํ๋๊ณผ ๋ฐํ์ด ๋น ๋ฅด๊ฒ ์ด์ด์ง๊ธฐ ๋๋ฌธ์ด๋ค
๋ฐ๋๋ก ๋งํ๋ฉด ๊ฒฝํฉ์ํฉ์ด ๊ธธ์ด์ง๋ค๋ฉด ๋ฝ์ ํ๋ํ๋ ์๊ฐ์ด ๊ธธ์ด์ง๊ธฐ์ cpu์ ์ ์จ์์น