unique_lock 이란?

 

  • unique_lock 객체는 lock_guard와 같이 RAII 기법을 통해 생성자에서 락에 대한 소유권을 획득하고 소멸자에서 락에 대한 소유권을 반환하는 객체입니다.

 

  • unique_lock을 생성할 때 derfer_lock, try_to_lock, adopt_lock 중 하나를 추가로 전달하여 lock_guard보다 다양한 기능을 사용할 수 있습니다.

 

  • lock_guard가 상대적으로 unique_lock 보다 더 가볍기 때문에 lock_guard를 우선적으로 사용하고 필요시에 unique_lock을 사용하는 것이 좋습니다.




defer_lcok

 

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

int num;
std::mutex m;

void WorkerThread() {
    for (int i = 0; i < 100000; ++i)
    {
        std::unique_lock<std::mutex> lk(m, std::defer_lock);
        lk.lock();

        ++num;
    }

    return;
}


int main()
{
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • defer_lock을 전달할 경우 소유권 획득 시점을 생성자가 아닌 lock 멤버 함수를 호출하는 시점으로 연기할 수 있습니다. 그리고 lock이 호출되었다면, 소멸자에서 이를 반환합니다.




try_to_lock

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

int num;
std::mutex m;

void WorkerThread() {
    for (int i = 0; i < 100000; ++i)
    {
        std::unique_lock<std::mutex> lk(m, std::try_to_lock);
        if (!lk.owns_lock()) {
            --i;
            continue;
        }

        ++num;
    }

    return;
}


int main()
{
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • try_to_lock은 생성자에서 lock에 대한 소유권을 획득할 수 있는지 여부를 시도를 합니다. 만약, 생성자에서 lock에 대한 소유권을 획득하였다면, owns_lock() 멤버 함수의 return 값이 true이고 획득하지 못했다면 false를 return 합니다.




adopt_lock

 

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

int num;
std::mutex m;

void WorkerThread() {
    for (int i = 0; i < 100000; ++i)
    {
        m.lock();
        std::unique_lock<std::mutex> lk(m, std::adopt_lock);
        ++num;
    }

    return;
}


int main()
{
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • adopt_lock는 이미 매개변수로 전달한 동기화 객체에 대한 소유권이 이미 획득되어 있는 상태이고, 이를 소멸자에서 반환하기 위해서 사용합니다.




RAII( Resource Acquisition Is Initialization )이란?


  • RAII( Resource Acquisition Is Initialization )는 리소스 획득은 초기화다 라는 의미를 가지고 있습니다. RAII 기법은 생성자에서 리소스를 획득하고 해당 인스턴스가 스코프를 벗어나면 자동으로 소멸자가 호출되면서 리소스를 해제하는 기법을 말합니다. RAII 패턴이 있기 때문에 C++의 창시자 비야네 스트롭스트룹은 다른 언어에서 사용되는 try/catch에서 finally를 C++에 도입하지 않는다고 하였습니다.




RAII 사용 예시


#include <iostream>
#include <Windows.h>

CRITICAL_SECTION sc;

class CSC {
public:

    CSC(CRITICAL_SECTION& sc)
        :mSC(sc)
    {
        std::cout << "Enter~\n";
        EnterCriticalSection(&mSC);
    }

    ~CSC()
    {
        std::cout << "Leave~\n";
        LeaveCriticalSection(&mSC);
    }

private:

    CRITICAL_SECTION& mSC;
};

int main()
{
    InitializeCriticalSection(&sc);

    {
        CSC sc(sc);
    }

    return 1;
}


  • 위와 같이 동기화 객체를 사용할 때 RAII 디자인 패턴을 사용하면 프로그래머의 실수로 동기화 객체를 해제하는 것을 깜빡하는 실수를 없앨 수 있습니다.

  • 스마트 포인터 또한 RAII 디자인 패턴을 바탕으로 만들어졌습니다.




+ Recent posts