namespace ServerCore
{
//재귀적 락을 허용할지 (yes) writeLock ->writeLock (ok) ,writelock->readlock (ok) ,readlock ->writelock (no)
//재귀적 락을 허용할지 (NO)
//스핀락 정책 (5000번 ->Yield)
class Lock
{
const int EMPTY_FLAG = 0x00000000;
const int WRITE_MASK = 0x7FFF0000;
const int READ_MASK = 0x0000FFFF;
const int MAX_SPIN_COUNT = 5000;
// [Unused (1비트)] [WriteThreadId (15비트)] [ReadCount (16비트)]
int flag= EMPTY_FLAG;
int writeCount = 0; //(yes)
public void WriteLock()
{
//동일 스레드가 writeLcok을 이미 흭득하고 있느지 확인 //(yes)
int lockThreadId = (flag & WRITE_MASK) >> 16; //(yes)
if (Thread.CurrentThread.ManagedThreadId == lockThreadId) //(yes)
{
writeCount++; //(yes)
return; //(yes)
}
//아무도 WriteLock Or ReadLock을 흭득하고 있지 않을 때, 경합해서 소유권을 얻는다.
//
//16비트만큼 밀어줘서 WriteThreadId를 사용할 수 있게 그리고 & WRITE_MASK 함으로써 쓸모없는 부분 다 0으로 바꿈
int desired = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK;
while (true)
{
for(int i=0; i < MAX_SPIN_COUNT; i++)
{
//시도를 해서 성공하면 return
//if (flag == EMPTY_FLAG) //아무도 락이 없는 상황
// flag = desired; //WriteThreadI에 자신의 Id를 채워주고싶음
// But 멀티스레드환경에서 스레드들이 동시에 if문을 실행하면 문제 생김 그래서 인터락드써야함
if (Interlocked.CompareExchange(ref flag, desired, EMPTY_FLAG) == EMPTY_FLAG)
{
writeCount = 1; //(yes)
return;
}
}
Thread.Yield();
}
}
public void WriteUnlock()
{
int lockCount = --writeCount; //(yes) 무조건 언락 하는것이 아니라 writeCount가 0이면 언락해드림
if (lockCount ==0) //(yes)
//당연한 말이지만 WriteLock한 애만 언락 가능
//이건 초기상태로 만들면 됨
Interlocked.Exchange(ref flag, EMPTY_FLAG);
}
public void ReadLock() //상호 배타가 아님!
{
//동일 스레드가 writeLcok을 이미 흭득하고 있느지 확인 //(yes)
int lockThreadId = (flag & WRITE_MASK) >> 16; //(yes)
if (Thread.CurrentThread.ManagedThreadId == lockThreadId) //(yes)
{
Interlocked.Increment(ref flag); //(yes) 리드카운트만 1 늘림
return; //(yes)
}
//아무도 WriteLock을 흭득하고 있지 않으면, ReadCount를 1 늘린다
while (true)
{
for(int i=0; i < MAX_SPIN_COUNT; i++)
{
//if ((flag & WRITE_MASK) == 0) //아무도 WriteLock을 흭득하고있지 않다는 의미
//{
// flag += 1; //이 방법도 멀티스레드환경에서 문제있음, 여기서 다른애가 WriteLock을 하면 [WriteThreadI)] [ReadCount] 둘다에 접근됨;;
// return;
//}
int expected = (flag & WRITE_MASK);
if (Interlocked.CompareExchange(ref flag, expected + 1, expected) == expected)
return;
}
Thread.Yield();
}
}
public void ReadUnlock()
{
Interlocked.Decrement(ref flag); //그냥 1줄여주면 끝
}
//writelock 하고 readlock 하면 readUnlock 먼저하고 writeUnlock을 해야함
}
}
namespace ServerCore
{
class Program
{
static volatile int count = 0;
static Lock _lock = new Lock();
static void Main(string[] args)
{
Task t1 = new Task(delegate ()
{
for (int i = 0; i < 100000; i++)
{
_lock.WriteLock();
count++;
_lock.WriteUnlock();
}
});
Task t2 = new Task(delegate ()
{
for (int i = 0; i < 100000; i++)
{
_lock.WriteLock();
count--;
_lock.WriteUnlock();
}
});
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine(count);
}
}
}
결과 값 : 0
'게임서버' 카테고리의 다른 글
하드웨어 최적화 (0) | 2020.11.30 |
---|---|
캐시 이론 (0) | 2020.11.30 |
컴파일러 최적화 (0) | 2020.11.30 |
쓰레드 기초 (0) | 2020.11.30 |