재귀적LOCK 비허용 버전
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ServerCore
{
/// <summary>
/// 재귀적 락을 허용할지 (No)(쉽게하기위해 No로 구현)
/// 스핀락 정책 (5000번-> Yield)
/// </summary>
class Lock
{
/// <summary>
/// 총 32비트
/// 0000 0000 0000 0000 16
/// 0000 0000 0000 0000 16
/// </summary>
const int EMPTY_FLAG = 0x00000000;
/// <summary>
/// 0111 1111 1111 1111
/// 0000 0000 0000 0000///여기사용
/// </summary>
const int WRITE_MASK = 0x7FFF0000;
const int READ_MASK = 0x0000FFFF;
/// <summary>
/// 0000 0000 0000 0000 ///여기사용
/// 1111 1111 1111 1111
/// </summary>
const int MAX_SPIN_COUNT = 5000;
int _flag = EMPTY_FLAG;
/// <summary>
/// // int 32비트
/// [Unused(1)] [WriteThreadid(15)][ReadCount(16)]
/// 첫비트는 사용안함(Unused)
/// WriteThreadid(15) 획득한 thread 기록용
/// ReadCount(16) readLock을 획득했을때 카운팅용
/// 시작값은 0으로 맞춰준다
/// </summary>
public void WriteLock()
{
// 아무도 WriteLock or ReadLock을 획득하고 있지 않을 때, 경합해서 소유권을 얻는다
///Thread.CurrentThread.ManagedThreadId 를 ->[WriteThreadid(15)] 여기에 넣기위해
/// 16비트만큼 밀어준다 <<16
/// 혹시 모르니까 & WRITE_MASK 해서 초과하는 값이 있으면 0으로 밀어주기
/// [Unused(1)] 이부분과 [ReadCount(16)] 이부분은 0으로 변경해주기!
int desired = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK;
while (true)
{
for (int i = 0; i < MAX_SPIN_COUNT; i++)
{
// 시도를 해서 성공하면 return
///Interlocked.CompareExchange(ref _flag, desired, EMPTY_FLAG) 이 부분은 이전값을 리턴하므로
///EMPTY_FLAG를 리턴값으로 받으면 정확히 비어있던 객체가 맞으니 통과!
///승자의 desired값이 _flag에 들어가게된다
///그러면 다른애들은 들어올수없다! (EMPTY_FLAG)와 같을수없음!
if (Interlocked.CompareExchange(ref _flag, desired, EMPTY_FLAG) == EMPTY_FLAG)
return;
///의사코드 모습
///실제로는 작동못함 원자성실패때문!
/// if (_flag == EMPTY_FLAG) // 두개로 나뉘어 져있어서 동시에 통과해서 에러날가능성 다분!
/// _flag = desired; //카운트 ++ 문제와 마찬가지로 여러가지 desired값이 _flag에 들어가게됨!!
///의사코드 모습
}
Thread.Yield();
}
}
public void WriteUnLock()
{
Interlocked.Exchange(ref _flag, EMPTY_FLAG);///EMPTY_FLAG로 비워둔다(초기상태로 변경)
}
public void ReadLock()
{
///아무도 WriteLock을 획득하고 있지않으면, ReadCount를 1 늘린다.
while (true)
{
for (int i = 0; i < MAX_SPIN_COUNT; i++)
{
///처음보면 이해가 존나안감;; 나도 이해가안감;
/// LockFree 프로그램의 기초라고 할수있으니 이해해보도록 노력하자
/// 경우 1 writeLock을 누가 잡고있다면
///
/// [WriteThreadid(15)] 이부분이 0이 아니기때문에
/// expected 값이 맞지 않으니 무조건 false ! ---> _flag != _flag & READ_MASK
/// 경우 2 writelock은 안쓰고 있는데 동시에 여러명이 ReadLock을 획득하려고 할 때!
///
/// 처음 expected를 뽑아올때는 전부 성공할것이다
/// 하지만 가장 처음 들어온 녀석만 +1을 성공하고
/// 나머지 녀석들은 expected와 같지않으니(예상한값과 다름) +1을 하지못하고 false
///
/// A,B 두녀석이 ReadLock획득을 위해 경쟁하고 있다고 치면 이런모양
/// 1. A(0) B(0)
/// 2. A(0->1) B(0->1)
/// 3. 빠른사람이 성공
/// 3.1 A가 빨랏으면 B(1->1)이런모습이 되서 실패됨
/// 3.1 B가 빨랏으면 A(1->1)이런모습이 되서 실패됨
int expected = (_flag & READ_MASK);
if (Interlocked.CompareExchange(ref _flag, expected+1, expected) == expected)
return;
///의사코드
///원자성 때문에 이것도 문제발생!!
//if ((_flag & WRITE_MASK) == 0)
//{
// ///아무도 writeLock을 획득하고있지않음!
// _flag = _flag + 1;
// return;
//}
///의사코드
}
Thread.Yield();
}
}
public void ReadUnLock()
{
Interlocked.Decrement(ref _flag);
}
}
}
재귀락 허용버전
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ServerCore
{
/// <summary>
/// 재귀적 락을 허용할지 (Yes) -> WriteLokc->WriteLock OK, WriteLock->ReadLock OK, ReadLock->WriteLock No
/// 스핀락 정책 (5000번-> Yield)
/// </summary>
class Lock
{
/// <summary>
/// 총 32비트
/// 0000 0000 0000 0000 16
/// 0000 0000 0000 0000 16
/// </summary>
const int EMPTY_FLAG = 0x00000000;
/// <summary>
/// 0111 1111 1111 1111
/// 0000 0000 0000 0000///여기사용
/// </summary>
const int WRITE_MASK = 0x7FFF0000;
const int READ_MASK = 0x0000FFFF;
/// <summary>
/// 0000 0000 0000 0000 ///여기사용
/// 1111 1111 1111 1111
/// </summary>
const int MAX_SPIN_COUNT = 5000;
int _flag = EMPTY_FLAG;
/// <summary>
/// // int 32비트
/// [Unused(1)] [WriteThreadid(15)][ReadCount(16)]
/// 첫비트는 사용안함(Unused)
/// WriteThreadid(15) 획득한 thread 기록용
/// ReadCount(16) readLock을 획득했을때 카운팅용
/// 시작값은 0으로 맞춰준다
/// </summary>
///
int _writeCount = 0;
/// <summary>
/// 멀티스레드 문제가 있을수 없으므로 flag로 뺄필요없음!!
/// 상호배타적인 writeLock의 숫자를 관리하기위해 추가(재귀락 허용버전)
/// </summary>
public void WriteLock()
{
///동일 쓰레드가 writeLock을 이미 획득하고 있는지 확인
int lockThreadId = (_flag & WRITE_MASK) >> 16;
if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
{
///획득하고 있다면 count 를 1만큼 늘이고 리턴 하지만 이것도 원자성 먹나?
_writeCount++;
return;
}
// 아무도 WriteLock or ReadLock을 획득하고 있지 않을 때, 경합해서 소유권을 얻는다
///Thread.CurrentThread.ManagedThreadId 를 ->[WriteThreadid(15)] 여기에 넣기위해
/// 16비트만큼 밀어준다 <<16
/// 혹시 모르니까 & WRITE_MASK 해서 초과하는 값이 있으면 0으로 밀어주기
/// [Unused(1)] 이부분과 [ReadCount(16)] 이부분은 0으로 변경해주기!
int desired = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK;
while (true)
{
for (int i = 0; i < MAX_SPIN_COUNT; i++)
{
// 시도를 해서 성공하면 return
///Interlocked.CompareExchange(ref _flag, desired, EMPTY_FLAG) 이 부분은 이전값을 리턴하므로
///EMPTY_FLAG를 리턴값으로 받으면 정확히 비어있던 객체가 맞으니 통과!
///승자의 desired값이 _flag에 들어가게된다
///그러면 다른애들은 들어올수없다! (EMPTY_FLAG)와 같을수없음!
if (Interlocked.CompareExchange(ref _flag, desired, EMPTY_FLAG) == EMPTY_FLAG)
{
_writeCount++;
return;
}
///의사코드 모습
///실제로는 작동못함 원자성실패때문!
/// if (_flag == EMPTY_FLAG) // 두개로 나뉘어 져있어서 동시에 통과해서 에러날가능성 다분!
/// _flag = desired; //카운트 ++ 문제와 마찬가지로 여러가지 desired값이 _flag에 들어가게됨!!
///의사코드 모습
}
Thread.Yield();
}
}
public void WriteUnLock()
{
int lockCount = --_writeCount;
if (lockCount == 0)
{
///실행한 만큼 짝이 맞아야지만 실행이되서 Lock이 풀리는 구조!
Interlocked.Exchange(ref _flag, EMPTY_FLAG);///EMPTY_FLAG로 비워둔다(초기상태로 변경)
}
}
public void ReadLock()
{
int lockThreadId = (_flag & WRITE_MASK) >> 16;
if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
{
///획득하고 있다면 count 를 1만큼 늘이고 리턴 하지만 이것도 원자성 먹나?
Interlocked.Increment(ref _flag);
return;
}
///아무도 WriteLock을 획득하고 있지않으면, ReadCount를 1 늘린다.
while (true)
{
for (int i = 0; i < MAX_SPIN_COUNT; i++)
{
///처음보면 이해가 존나안감;; 나도 이해가안감;
/// LockFree 프로그램의 기초라고 할수있으니 이해해보도록 노력하자
/// 경우 1 writeLock을 누가 잡고있다면
///
/// [WriteThreadid(15)] 이부분이 0이 아니기때문에
/// expected => writeThread값이 0 이니까 지금의 _flag(writeLock이 잡힌)과 맞지 않으니 통과안됨!
/// expected 값이 맞지 않으니 무조건 false ! ---> _flag != _flag & READ_MASK
/// 경우 2 writelock은 안쓰고 있는데 동시에 여러명이 ReadLock을 획득하려고 할 때!
///
/// 처음 expected를 뽑아올때는 전부 성공할것이다
/// 하지만 가장 처음 들어온 녀석만 +1을 성공하고 (expected 값이 _flag와 같음! +1이 안됫으니까)
/// 나머지 녀석들은 expected와 같지않으니(예상한값과 다름) +1을 하지못하고 false
///
/// A,B 두녀석이 ReadLock획득을 위해 경쟁하고 있다고 치면 이런모양
/// 1. A(0) B(0)
/// 2. A(0->1) B(0->1)
/// 3. 빠른사람이 성공
/// 3.1 A가 빨랏으면 B(1->1)이런모습이 되서 실패됨
/// 3.1 B가 빨랏으면 A(1->1)이런모습이 되서 실패됨
int expected = (_flag & READ_MASK); ///현재상황에서 보는값은 READ_MASK를 씌운값이다(뒤에 FFFF이것만 본다)
///WriteFlag에 해당하는값은 0으로 밀어버린것
///이말은 writeFlag가 전부 0이라는 소리다! == > (_flag & WRITE_MASK) == 0 이부분임!!!
/// (_flag & WRITE_MASK) == 0 이부분은 완전히 사라진 이유는 READ_MASK를 씌우면 0이되니까!
///정리하자면
///원하는 값 expected는
///WRITE_MASK는 0 , READ_MASK는 READ_MASK를 씌운값
///그래서 WRITE_MASK가 없다면 READ_MASK에 1을 더해주고 끝!
///[Unused(1)] [WriteThreadid(15)][ReadCount(16)]
if (Interlocked.CompareExchange(ref _flag, expected+1, expected) == expected)
return;
///의사코드
///원자성 때문에 이것도 문제발생!!
//if ((_flag & WRITE_MASK) == 0)
//{
// ///아무도 writeLock을 획득하고있지않음!
// _flag = _flag + 1;
// return;
//}
///의사코드
}
Thread.Yield();
}
}
public void ReadUnLock()
{
Interlocked.Decrement(ref _flag);
}
}
}
댓글 없음:
댓글 쓰기