lock

ㅋㅋ·2022년 10월 27일

csharp게임서버

목록 보기
3/16

레이스 컨디션 문제를 해결하기 위하여 사용


  • interlocked
Interlocked.Increment(ref x);
Interlocked.Decrement(ref x);
Interlocked.Add(ref x, 10);
int origin = Interlocked.Exchange(ref x, 1);
int origin2 = Interlocked.CompareExchange(ref x, 1, 0); // CAS Compare-And-Swap

주로 정수형 데이터를 수정할 때 사용


  • Monitor
static object key = new object();
static void TestThread1()
{
    try
    {
        Monitor.Enter(key);

        x++;
    }
    finally
    {
        Monitor.Exit(key);
    }

}

Enter와 Exit의 짝을 항상 맞춰야 함

TryEnter 함수로 일정 시간동안 lock을 시도하는 함수를 사용할 수 있음


  • Lock
lock (key)
{
    x++;
}

Monitor와 같지만 Enter, Exit을 신경쓰지 않아도 된다.

해당 구문을 빠져나올 시 자동으로 release


  • spinlock

lock을 잡을 때까지 계속하여 시도함 (busy waiting)

context switching의 비용을 생각하여 계속 시도하는 것이 더 효율적일 수 있음

class CustomSpinLock
{
    volatile int _lockCount = 0;

    public void Acquire()
    {
        while (true)
        {
            if (Interlocked.CompareExchange(ref _lockCount, 1, 0) == 0)
            {
                return;
            }
        }
    }

    public void Release()
    {
        Interlocked.Exchange(ref _lockCount, 0);
    }
}

C#에 기본적으로 구현 되어 있음

그러나 전혀 양보하지 않고 시도하는게 아니라 어느정도 시도 후 양보

static SpinLock _spinLock = new SpinLock();
static void TestThread1()
{
    bool lockTaken = false;
    try
    {
        _spinLock.Enter(ref lockTaken);
        x++;
    }
    finally
    {
        if (lockTaken)
        {
            _spinLock.Exit();
        }
    }
}

  • yield

    lock 시도 후 스레드를 양보함

Thread.Sleep(1); // 1ms만큼 휴식하길 원하지만 시간이 어떻게될지는 os 마음
Thread.Sleep(0); // 스레드 우선순위를 계산하여 나와 순위가 같거나 높은 스레드에게 양보
Thread.Yield(); // 실행이 가능한 스레드에게 양보

  • AutoResetEvent

커널에서 관리하는 lock을 이용하며 사용 가능하게 될 때

기다리고 있는 스레드들에게 이벤트 시그널을 날려줌

=> 커널까지 통신해야 해서 다른 lock과 비교적 속도가 느림

class EventLock
{
    AutoResetEvent _lock = new AutoResetEvent(true);
    // initial state: true 바로 사용 가능, false 바로 사용 불가능
        
    public void Acquire()
    {
        _lock.WaitOne(); // AutoReset => _lock.Reset()을 한번에 실행
    }

    public void Release()
    {
        _lock.Set();
    }
}

  • Mutex

마찬가지로 커널을 이용한 동기화 변수로 스레드 id나 잠긴 회수등의 추가 정보를 가짐

멀티 프로세스에서 사용 가능


static Mutex _lock = new Mutex();
static void TestThread2()
{
    for (int i = 0; i < 10000; i++)
    {
        _lock.WaitOne();
        y++;
        _lock.ReleaseMutex();
    }
}
  • ReaderWriterLock RWLock

메모리의 read, write를 따로 지정하여 잠글 수 있음

writer가 없을 시 다수의 스레드가 메모리에 접근하여 read할 수 있음

static ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
static void TestThread2()
{
    for (int i = 0; i < 10000; i++)
    {
        _lock.EnterReadLock();
        int temp = y + 1;
        _lock.ExitReadLock();
    }
}

0개의 댓글