기록은 기억을 이기고 시간보다 오래 남는다.

게임 개발/THREAD

Lock 이란

준_준 2024. 5. 29. 14:45

 

만약 하나의 공유 자원에 여러 개의 쓰레드가 접근을 했을 경우

그 자원의 값이 뒤죽 박죽이 될 수 있으니 한 쓰레드가 그 자원을 독점 할 수 있도록 도와 준다.

 

Lock 구현 방법으로는 3가지가 있다.

 

1.Spin lock

   - 이 방법은 해당 자원의 임계영역이 풀릴 떄 까지 무작정 기다리는 것을 말한다.

   - 무식한 방법이지만 임계영역이 금방 풀릴 것이 기대가 있다면 사용하기 좋다. 

      하지만 임계영역이 풀리지 않는 다면 매우 비효율적이다.

   - 하지만 cpu 점유가 높아 지고 다른 쓰레드를 방해할 수 있다.

아래는 Spin lock의 예시이다.

#include <iostream>
#include <thread>
#include <atomic>

std::atomic_flag lock = ATOMIC_FLAG_INIT; // std::atomic_flag 객체 생성

void print(int num)
{
    while (lock.test_and_set(std::memory_order_acquire)) // 뮤텍스 잠금
    {
        // 뮤텍스가 해제될 때까지 busy-waiting
    }
    std::cout << "Thread " << num << " started" << std::endl;
    lock.clear(std::memory_order_release); // 뮤텍스 해제
}

int main()
{
    std::thread t1(print, 1);
    std::thread t2(print, 2);
    t1.join();
    t2.join();
    return 0;
}

 

2. Sleep

   - 랜덤적으로 대기하는 것을 말한다. 

   - 효율적으로 보이지만 운이 좋지 않으면 계속 대기 할 수도 있따.

   - context switching이 발생해 그 만큼 이동거리가 발생한다.

#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>

std::mutex mtx;

void print(int num) {
    bool lockAcquired = false;
    do {
        mtx.lock();
        if (lockAcquired) {
            std::cout << "Thread " << num << " critical section end" << std::endl;
            mtx.unlock();
            break;
        }
        std::cout << "Thread " << num << " critical section start" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1)); // 1초 동안 sleep
        lockAcquired = true;
    } while (!lockAcquired);
}

int main() {
    std::thread t1(print, 1);
    std::thread t2(print, 2);

    t1.join();
    t2.join();

    return 0;
}

 

3. Event

   - 해당 쓰레드에게는 효과적인 방법 일 수 있지만 다른 리소스를 사용한다.

   - 이 방법도 context switching이 발생한다.

 

이벤트는 추후에 구현하도록 하겠다.

 

 

 

 

반응형