1. ๋ค์ด๊ฐ๋ฉฐ
๊ฒฝํฉ์กฐ๊ฑด์์๋ lock ํค์๋๋ก ๋ง์๋ฒ๋ฆฌ๋ฉด ๋๋ค๋ผ๊ณ ์๊ฐํ์ง๋ง
๊ฒ์์์๋ ์ฑ๋ฅ์ด ์๋ช
์ด๋ผ ๋ชจ๋ ์ ๊ทผ์ ๋ง๋ ๊ฑด ๋ญ๋น์ผ ์ ์๋ค.
์๋ฅผ ๋ค์ด ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ฝ๊ธฐ๋ง ํ๋ ๊ฒฝ์ฐ๋ ์์ ํ๋ฐ, ๊ตณ์ด ํ ์ค์ฉ ์ค ์ธ์ฐ๊ฒ ํ ํ์๊ฐ ์๋ค.
๊ทธ๋์ ๋ฑ์ฅํ๋ ๊ฒ Read-Write Lock์ด๋ค.
-
Read๋ ๋์์ ์ฌ๋ฌ ๋ช OK
-
Write๋ ์ค์ง ํ ๋ช ๋ง, ๋ ์ !
๋ฉํฐ์ค๋ ๋๋ก์ง์ด์๋๋ฐ Read๋ก์ง์ด 99.999%, Write๋ก์ง์ด 0.0001%๋ก ๋ฐ์ํ๋ค ๊ฐ์ ํ๋ค ๊ทธ๋ฌ๋ฉด Read๋ง ์ฃผ๊ตฌ์ฅ์ฐฝํ๋๋ฐ ๊ตณ์ด lock์ ๊ณ์ํด์ ์ธํ์๊ฐ ์๋ค ์ฑ๋ฅ์ ์ด์ ์ ์ฑ๊ธฐ๋ ค๋ฉด writeํ ๋๋ง read๋ lock์ ๊ฑธ๋ฆฌ๋ ๋ฐฉ์์ผ๋ก ํ๋ฉด ์ฑ๋ฅ์ ์ผ๋ก ์ด๋์ ๋ณด๊ฒ๋๋ค
์ฆ, **Read-Write Lock์ โ์ฝ์ ๋ ์์ ๋กญ๊ฒ, ์ธ ๋ ๋ ์ ์ ์ผ๋กโ**๋ผ๋ ๊ท์น์ผ๋ก ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ๋ชจ๋ ์ฑ๊ธธ ์ ์๋ ๋๊ตฌ๋ค.
2. Read-Write Lock ๊ธฐ๋ณธ ๊ฐ๋
-
Read Lock:
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ฝ๊ธฐ๋ง ํ๋ ๊ฒ์ ์์ ํ๋ฏ๋ก, ์ฌ๋ฌ ๊ฐ๊ฐ ๋์์ ๋ค์ด์ฌ ์ ์์ต๋๋ค. -
Write Lock:
๋ฐ์ดํฐ๋ฅผ ์์ ํ ๋๋ ๋จ ํ ์ค๋ ๋๋ง ์ ๊ทผํ ์ ์๋๋ก ๋ณด์ฅํด์ผ ํฉ๋๋ค. -
์ ์ฝ:
-
์ฐ๋ ๋์์๋ ์๋ฌด๋ ์ฝ๊ฑฐ๋ ์ธ ์ ์์.
-
์ฝ๋ ์ค์๋ ๋ค๋ฅธ ์ฝ๊ธฐ ์ค๋ ๋๋ ํ์ฉ, ํ์ง๋ง ์ฐ๊ธฐ๋ ๋ถ๊ฐ๋ฅ.
-
์ฌ๊ท์ ๋ฝ(Recursive Lock) ์ง์ ์ฌ๋ถ๋ ๊ตฌํ์ ๋ฐ๋ผ ๋ค๋ฆ.
-
3. ๊ตฌํ ์์ด๋์ด
3.1 Flag ๊ตฌ์กฐ
๋ฝ ์ํ๋ฅผ ๋ํ๋ด๊ธฐ ์ํด 32๋นํธ int ํ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
[Unused(1bit)] [WriteThreadId(15bit)] [ReadCount(16bit)]
-
ReadCount (ํ์ 16๋นํธ) : ํ์ฌ ๋ช ๊ฐ์ Reader๊ฐ ์ ๊ทผ ์ค์ธ์ง ์ ์ฅ
-
WriteThreadId (์ค๊ฐ 15๋นํธ) : WriteLock์ ์ก์ Thread ID ์ ์ฅ
-
Unused (์์ 1๋นํธ) : ์๋น ๋นํธ(์ถํ ๊ธฐ๋ฅ ํ์ฅ์ฉ)
//๋ณ์ ์๊ฐ
const int EMPTY_FLAG = 0x00000000;
const int WRITE_MASK = 0x7FFF0000;
const int READ_MASK = 0x0000FFFF;
const int MAX_SPIN_COUNT = 5000;
// [Unused(1 bit)] [WriteThreadId(15 bit)] [ReadCount(16 bit)]
// ReadCount -> ์ฐ๋ฆฌ๊ฐ ReadLockํ๋ํ๋ฉด ์ฌ๋ฌ์ ๋ค์ด Readํ ์์์ผ๋ ์นด์ดํ
ํ๋๊ฑฐ๊ณ
// Write -> ํ๋ฒ์ ํ ์ค๋ ๋๋ง ํ๋ํ ์์๋ค ๊ทธ์น๊ตฌ๊ฐ ๋๊ตฌ์ธ์ง id์ ์ฅ
int flag = EMPTY_FLAG;
//ํ๋๊ทธ๊ฐ ํ์์๋์ด์
//๋๊ตฐ๊ฐ๊ฐ write๋ฅผ ์ก์๋ค๋๊ฑด ๋ค๋ฅธ์ ๋์๋ค ์ค์ง ํ๋๋ง ์ก๋๊ฒ
int writeCount = 0;
๐ ์ฅ์ : int ํ๋๋ก ๊ด๋ฆฌํ ์ ์์ด Interlocked.CompareExchange ๊ฐ์ ์์์ ์ฐ์ฐ์ ํ์ฉํ ์ ์์ต๋๋ค.
3.2 Write Lock
public void WriteLock()
{
// ์ฌ๊ท ๋ฝ
// ๋์ผ ์ฐ๋ ๋๊ฐ writeLock์ ์ด๋ฏธ ํ๋ํ๊ณ ์๋์ง ํ์ธ
int lockThreadId = (flag & WRITE_MASK) >> 16;
if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
{
writeCount++;
return;
}
//----------------------------------------
//์๋ฌด๋ WriteLock or ReadLock์ ํ๋ํ๊ณ ์์ง ์์ ๋, ๊ฒฝํฉํด์ ์์ ๊ถ์ ์ป๋๋ค.
// Thread.CurrentThread.ManagedThreadId == ๋ณธ์ธ ์ค๋ ๋ ์์ด๋
// << 16 ํด์ฃผ๋ ์ด์ WRTIE_MASK๊ฐ 16๋นํธ์ดํ๋ถํฐ ๊ฐ์์ฐ๊ธฐ์ ๋ฐ์ด์ฃผ๋๊ฒ
// & ์ฐ์ฐ์ ํตํด์ WRITE_MASK ๊ฐ์ด ์๋๊ฒ์ ๋ค ๋ฐ์ด์ค๋ค ํน์ ๋ชจ๋ฅด๋
// ๋ด ์ค๋ ๋ ์์ด๋๋ง ๋นํธ์ ๋ค์ด๊ฐ๊ธธ ์ํ๋ desired๊ฐ ๋๊ฒ
int desired = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK;
while(true)
{
for(int i =0; i < MAX_SPIN_COUNT; i++)
{
if (EMPTY_FLAG == Interlocked.CompareExchange(ref flag, desired, EMPTY_FLAG))
{
writeCount = 1;
return;
}
}
Thread.Yield();
}
}-
์ฌ๊ท ๋ฝ ํ์ฉ: ๋์ผ ์ค๋ ๋๊ฐ ์ฌ๋ฌ ๋ฒ WriteLock์ ์ก์ ์ ์์.
-
๋ค๋ฅธ ์ค๋ ๋๊ฐ ์ฐ๊ณ ์๊ฑฐ๋ ์ฝ๊ณ ์์ผ๋ฉด ์คํจ โ ์คํ ํ ์ฌ์๋.
3.3 Read Lock
public void ReadLock()
{
//@@write@@๊ฐ ์ด๋ฏธ ์ก๊ณ ์์๋๋ง readCount๋ฅผ ๋๋ฆฌ๋๊ฒ
int lockThreadId = (flag & WRITE_MASK) >> 16;
if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
{
Interlocked.Increment(ref flag);
return;
}
// ์๋ฌด๋ WriteLock์ ํ๋ํ๊ณ ์์ง ์์ผ๋ฉด, ReadCount๋ฅผ 1 ๋๋ฆฐ๋ค
while (true)
{
for(int i =0; i < MAX_SPIN_COUNT; i++)
{
int expected = (flag & READ_MASK);
if (Interlocked.CompareExchange(ref flag, expected + 1, expected) == expected)
return;
}
Thread.Yield();
}
}
-
ํต์ฌ:
expected = flag & READ_MASK-
๋ผ์ดํฐ๊ฐ ์์ผ๋ฉด ์์ 15๋นํธ๊ฐ ์ฑ์์ ธ ์์ด
flag != expectedโ CAS ์คํจ โ ์ฝ๊ธฐ ์ฐจ๋จ -
๋ผ์ดํฐ๊ฐ ์์ผ๋ฉด
flag == expectedโ CAS ์ฑ๊ณต โ ReadCount +1
-
-
์ฌ๋ฌ Reader๊ฐ ๋์์ ๋ค์ด์๋ CAS๋ก ์์์ ์ฆ๊ฐ ๋ณด์ฅ.
3.4 Unlock
public void WriteUnLock()
{
int lockCount = --writeCount;
if (lockCount == 0)
Interlocked.Exchange(ref flag, EMPTY_FLAG);
}
public void ReadUnLock()
{
Interlocked.Decrement(ref flag);
}
-
Write๋ ์ฌ๊ท ํ์๋ฅผ ์ฒดํฌํ๊ณ 0์ด ๋๋ฉด ์์ ํ ํด์ .
-
Read๋ ๋จ์ํ ์นด์ดํธ ๊ฐ์.
4. ํ ์คํธ ์ฝ๋
static volatile int count = 0;
static Lock _lock = new Lock();
static void Main(string[] args)
{
Task t1 = new Task(() =>
{
for (int i = 0; i < 100000; ++i)
{
_lock.ReadLock();
count++;
_lock.ReadUnLock();
}
});
Task t2 = new Task(() =>
{
for (int i = 0; i < 100000; ++i)
{
_lock.ReadLock();
count--;
_lock.ReadUnLock();
}
});
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine(count);
}
5. ์ ๋ฆฌ
-
์ผ๋ฐ ๋ฝ : ํ์ฅ์ค ์ด์ 1๊ฐ. ๋๊ตฌ๋ 1๋ช ๋ง ๋ค์ด๊ฐ๊ณ , ๋์ฌ ๋๊น์ง ๋๊ธฐ.
-
Read-Write ๋ฝ :
-
์ผ๋ฐ์ธ์ ์ฝ๊ธฐ โ ์ฌ๋ฌ ๋ช ๋์์ ํ์ฅ์ค ๊ตฌ๊ฒฝ ๊ฐ๋ฅ
-
VIP๋ ์ฐ๊ธฐ โ VIP๊ฐ ๋ค์ด๊ฐ๋ฉด ๋ฌธ์ ๊ฑธ์ด ์ ๊ทธ๊ณ ํผ์๋ง ์ฌ์ฉ
-
VIP๊ฐ ์์ ๋๋ ๋ง์น ๋ฝ์ด ์๋ ๊ฒ์ฒ๋ผ ์์ ๋กญ๊ฒ ์ ์ฅ
-
6. ํท๊ฐ๋ฆด ์ ์๋ flag & READ_MASK ์ ๋ฆฌ
๐พ์ ์๊ฐํด๋ณด์ flag & READ_MASK๋ฅผ ํ๋ค์น์
- ๋ง์ฝ ๋๊ตฐ๊ฐ WRITE๋ฅผ ํด์ ์์ชฝ 0x7FFF0000๋ถ๋ถ์ด ๋ณ๊ฒฝ๋ ์ํ๋ผ๊ณ ๊ฐ์
- ๊ทธ๋ผ ํ์ฌ flag๋ 0x32CF0000์ด ๋์ด์๋ค๊ณ ๊ฐ์
- ํ์ฌ ์ฐ์ฐํ๊ณ ์๋ ๋ถ๋ถ์ READ_MASK์ ๋ถ๋ถ ์ฆ 0x0000FFFF์ด๋ถ๋ถ์ ์ฐ์ฐ์ค์ด๋ค
- WRITE๊ฐ์๋์ํ expected = 0x32CF0000 & 0000FFFF ์ํ๋ฉด ๊ฐ์ด ์ด๋ป๊ฒ๋์ฌ๊น?
- expected = 0x00000000์ด ๋์จ๋ค (์ฌ๊ธฐ๊น์ง๋ ์๋๋๊ฒ ๋ง๋ค RAED_MASK๋ง ์ฒดํฌํ ๊ฑฐ์ด๊ธฐ์)
- !!์ฌ๊ธฐ์ ์ค์!!
- CompareExchange ์ฌ๊ธฐ์๋ expected(0x00000000์ด) == flag(0x32CF0000)๊ฐ ๊ฐ์์ง ๋ฌผ์ด๋ณด๋๊ฒ
- flag๊ฐ 0x0000000์ด๋? ์๋๋ค! write๊ฐ ๋ฝ์ ์ก๊ณ ์์ด์ ์์ด์ 0x32CF0000๋ค!
- ๊ทธ๋์ ์คํจ๋ฅผ ํ๊ฒ๋๋ค! !! ๊ฒฐ๋ก : WRITEํ๋๋์ RAED๋ฅผ ๋ชปํ๊ฒ ํ๋ ค๋๊ฒ
๐พ๋ค์๋จ๊ณ READ๊ฒฝํฉ ์กฐ๊ฑด
- RAEDํ๋ ค๋ ๋ ์ค๋ ๋์ ๊ฒฝํฉ ์ํ๋ฅผ ์ํํ๊ฒ ์ ๋ฆฌํ๊ณ ์นด์ดํ ๋ฐฉ์
- int expected = (flag & READ_MASK) ๋ด๊ฐ ์ํ๋๊ฐ์ ๊ณ์ฐํ๋ค
- ๋ค๋ฅธ์ค๋ ๋๋ก ์ปจํ ์คํธ ์ค์์นญ์ ํ๋ค
- ๋์ค์ ๋ค๋ฅธ์ค๋ ๋๋ (flag & READ_MASK)์ฐ์ฐ์ ํ๊ฒ๋๋ค
- ๊ทธ๋ผ ์ฒซ๋ฒ์งธ ์ค๋ ๋์ expected๊ฐ๊ณผ ๋๋ฒ์งธ ์ค๋ ๋์ expected ๊ฐ์ด ๊ฐ๋ค
- ๊ทธ๋ผ ๋๋ฒ์งธ ์ค๋ ๋๊ฐ ๋จผ์ CompareExchange ํ๋ค์น๋ฉด ๋๋ฒ์งธ ์ค๋ ๋๋ ํต๊ณผํ๋ค
- ๋ค์์ ์ฒซ๋ฒ์งธ ์ค๋ ๋๋ก ์ปจํ ์คํธ ์ค์์นญ์ํ๋ค.
- ๊ทผ๋ ๋ด๊ฐ ์์ํ๋ flag๊ฐ expected ๊ฐ๊ณผ ๋ค๋ฅด๋ค ๊ทธ๋ฌ๋ฉด ํ๋ฒ ๋ ์ํ๋ฅผ ํ๊ฒ๋๋ค
- ๋ค์ ๋์์์ (flag & READ_MASK); ํ๊ฒ๋๊ณ ์ปจํ ์คํธ ์ค์์นญ์ํ๊ฒ๋๋ ๋ฌธ์ ๊ฐ์๋ค
- ์๋? ์ง๊ธ ๊ธฐ๋ค๋ฆฌ๊ณ ์๋ ์ค๋ ๋๊ฐ ์๊ธฐ๋๋ฌธ์ด๋ค.
- ๊ทธ๋์ ์ฒซ๋ฒ์งธ ์ค๋ ๋๋ ์ฑ๊ณตํ๊ฒ๋๋ฉด์ ๋ฝ์ ์ป๊ฒ๋๋ค