개념
구현하기에 앞서 결정 할 규칙
1. 재귀적 락
2. 스핀락 정책(5000번 루프를 돌고도 Lock을 획득할 수 없으면 잠시 스핀락을 중단)
참고 : https://redisson.org/glossary/java-readwritelock.html
코드
Lock에 사용될 상수, 변수
// 16진수 표현
// F(15) -> 이진수 1111, FFFF -> 이진수 1111 묶음 4개
const int EMPTY_FLAG = 0x00000000;
const int WRITE_MASK = 0x7FFF0000;
const int READ_MASK = 0x0000FFFF; // 최대 2의 16승 개의 읽기 스레드 생성가능
const int MAX_SPIN_COUNT = 5000;
// [Unused(1비트)] [WriteThreadId(15비트)] [ReadCount(16비트)]
// 초기값 : EMPTY_FLAG(LOCK 걸려있지 않는 상태)
public int _flag = EMPTY_FLAG;
WriteLock 함수
public void WriteLock() {
// &(AND 비트 연산 수행) 두 개의 피연산자의 비트가 모두 1일 경우에만 1을 반환
// 현재 쓰레드 ID를 가져와서 현재 위치에서(1비트) 왼쪽으로 16비트만큼 밀어준다
// [1비트][15비트][16비트] -> 쓰레드 ID는 16비트 자리부터 공간을 차지함
// WRITE_MASK의 비트가 [1비트 -> 0][15비트 -> 모두1] 이기 때문에 쓰레드 ID의 값만큼만 1로 표시
// WRITE_MASK의 비트 -> [0][111 1111 1111 1111]
// 쓰레드 ID -> 3(십진수) -> 11(이진수) 0 000 0000 0000 0011]
// 11]
int temp = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK;
while (true)
{
for (int i = 0; i < MAX_SPIN_COUNT; i++) {
// 첫번째 파라미터와 세번째 파라미터를 비교해서 동일하면 첫번째 파라미터 값을 두번째 파라미터로 변경
// 아무도 writeLock을 점유하지 않은 상태(_flag의 값이 0)라면 Lock을 획득(_flag에 쓰레드 ID를 넣음)하고 WriteLock 함수 종료
// "CompareExchange" 반환값이 변경하기 전의 값을 반환하므로 변경 전의 값이 "EMPTY_FLAG" 경우만 LOCK 획득 가능
if (Interlocked.CompareExchange(ref _flag, temp, EMPTY_FLAG) == EMPTY_FLAG) {
return;
}
}
Thread.Yield();
}
}
WriteLock 해제 함수
public void WriteUnLock() {
Interlocked.Exchange(ref _flag, EMPTY_FLAG);
}
쓰기 전용 Lock의 경우는 한 번에 오직 하나의 쓰레드만 접근이 가능하기 때문에 "_flag" 값을 0으로 밀어줌
(Lock(1) 또는 UnLock(0) 상태 총 2가지 경우만 존재)
ReadLock 함수
public void ReadLock() {
// 아무도 WriteLock을 점유하고 있지 않으면 ReadCount를 1 늘림
while (true)
{
for (int i = 0; i < MAX_SPIN_COUNT; i++)
{
// 쓰기 lock의 점유가 없는 경우에만 읽기 lock 획득 가능
// 여러 개의 스레드가 동시에 읽기 lock을 점유하려고 해도 순차적으로 lock을 획득
int expected = (_flag & READ_MASK);
// ex) A와 B 스레드가 동시에 접근해서 expected 값이 0이라고 해도
// A 스레드가 먼저 "Interlocked" 함수를 수행하면 flag 값이 1로 변경되기 때문에 그 다음으로 들어온 B 스레드는 flag 값이 1이므로 if 조건에 틀려버림
if (Interlocked.CompareExchange(ref _flag, expected + 1, expected) == expected)
{
return;
}
}
}
}
ReadLock 해제 함수
public void ReadUnLock()
{
Interlocked.Decrement(ref _flag);
}
동시에 여러 개의 쓰레드가 Read 작업을 할 수 있기 때문에 ReadLock을 획득한 쓰레드의 수를 감소
'게임개발 > 게임서버' 카테고리의 다른 글
멀티 쓰레드 프로그래밍 (0) | 2024.04.17 |
---|---|
멀티쓰레드 Lock 구현 방식 (2) | 2024.01.15 |
소켓 프로그래밍 (0) | 2023.10.29 |
TLS(Thread Local Storage) (0) | 2023.10.28 |
교착 상태(Deadlock) (0) | 2023.10.17 |