CodeNote
Coding Note
CodeNote
전체 방문자
오늘
어제
  • 전체 보기 (35)
    • C++ (33)
      • Modern C++ (12)
      • Modern C++ STL (0)
      • Thread (16)
      • Thread (Async) (5)
    • 디자인패턴 (0)
    • Algorithm (2)
    • Electron (0)
    • Python (0)

블로그 메뉴

  • 홈
  • Github
  • 태그
  • 방명록

공지사항

인기 글

태그

  • C++ #Memory
  • LOCK
  • mutex
  • Free
  • 자료구조
  • C++

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
CodeNote

Coding Note

[Thread] std::shared_mutex
C++/Thread

[Thread] std::shared_mutex

2022. 7. 17. 18:43

std::shared_mutex (C++17)

말 그대로 여러 thread가 mutex lock을 동시에 잡을 수 있는 mutex이다. 일반적인 mutex라고 한다면 mutual exclusion 특징을 가지고 있기 때문에 mutex lock을 획득하려는 여러 thread 중 하나의 thread만 lock을 획득하여 크리티컬 섹션 안으로 진입이 가능하다.

 

그리고 lock을 획득하지 못한 thread들은 block 상태로 대기하고 있게 된다. 반면에, std::shared_mutex는 이러한 일반적인 mutex기능을 가지고 있으면서 shared lock 기능 또한 가지고 있다.

 

그림 예

일반적인 lock이 아니라 shared lock을 획득한다고 하였을 때, 먼저 하나의 thread가 shared lock을 시도하여 크리티컬 섹션 안으로 진입한다. 이 때 shared lock을 시도한 다른 thread들은 block 상태에 들어가는 것이 아니라 같이 크리티컬 섹션 안으로 진입이 가능하다. 그 이후 진입한 모든 thread들이 크리티컬 섹션 밖으로 나가면서 lock을 해제해야 shared lock이 풀린다.

 

cppreference를 보면 shared_mutex는 (exclusive, shared) access를 가지고 있다. 즉, shared_mutex는 일반적인 lock을 획득하려는 thread와 shared lock을 획득하려는 thread 둘로 나뉘어 진다. 두 타입의 thread들 중 가장 먼저 lock을 획득한 thread가 어떤 것인지에 따라 shared_mutex의 response를 2개로 구분할 수 있다.

 

Exclusive한 thread가 먼저 Lock을 획득한 경우

나머지 thread는 모두 block이 되어 크리티컬 섹션에 들어갈 수 없다. 그리고 나서 먼저 들어간 thread가 unlock이 되면 block 상태에 있던 thread들 중 하나의 thread가 lock을 획득하면서 크리티컬 섹션안으로 진입한다.

 

Shared한 thread가 먼저 Lock을 획득한 경우

남아있는 thread들 중 shared lock을 획득하고자 하는 thread들은 동시에 크리티컬 섹션으로 진입이 가능하다. 이 때, exclusive lock을 획득하고자 하는 thread들은 block 상태가 된다. 그리고 나서 shared lock을 획득한 thread들이 unlock이 되면 exclusive lock을 획득하고자 하는 thread가 lock을 획득하고 기존과 같이 exclusive lock 메커니즘으로 동작한다.

 

이와 같은 내용으로 알 수 있듯이 접근 하는 동작에 따라 최적화가 가능한데 만약 접근하려는 모든 thread들이 read를 수행하려한다면 shared mutex를 사용함으로써 읽기 연산을 동시에 수행할 수 있다. 즉, write연산에는 exclusive lock, read 연산에는 shared lock을 사용한다면 훨씬 더 효율적인 mutex 사용이 가능한 것이다.

 

Example Code

struct MInt
{
    std::shared_mutex mtx;
    int num = 0;
}

void setNum(MInt& mi, int num)
{
    mi.mtx.lock();
    mi.num = num;
    mi.mtx.unlock();
}

void printNum(MInt& mi)
{
    mi.mtx.lock_shared();
    std::cout << mi.num << std::endl;
    mi.mtx.unlock_shared();
}

int main()
{
    MInt mi;
    
    std::thread t1(printNum, std::ref(mi));
    std::thread t2(setNum, std::ref(mi), 100);
    std::thread t3(printNum, std::ref(mi));
    std::thread t4(printNum, std::ref(mi));
    
    t1.join();
    t2.join();
    t3.join();
    t4.join();

    std::cout << "final num : " << mi.num << std::endl;

    return 0;
}

결과

printNum()은 read 연산이므로 lock_shared()를 사용했고, setNum은 write연산이므로 lock()을 사용하였다. 그런데 결과를 보면 실행을 할 때마다 값이 달라지는 것을 확인할 수 있다. 그 이유는 어떤 스레드가 가장 먼저 lock을 획득하는 지에 따라 결과가 달라지게되는 Race Condition 때문이다. 그렇지만 이 예제는 shared mutex를 이해하기 위한 내용이므로 아무런 문제가 없다. 

 

shared_mutex의 RAII 사용

당연하게도 위의 예제는 RAII를 사용하고 있지 않기 때문에 안전하게 RAII를 지원하는 lock manage를 사용해야한다. std::shared_lock을 사용하면 된다.

Example Code

struct MInt
{
    std::shared_mutex mtx;
    int num = 0;
}

void setNum(MInt& mi, int num)
{
    std::lock_guard<std::shared_mutex> lck(mi.mtx);
    mi.num = num;
}

void printNum(MInt& mi)
{
    std::shared_lock<std::shared_mutex> lck(mi.mtx);
    std::cout << mi.num << std::endl;
}

int main()
{
    MInt mi;
    
    std::thread t1(printNum, std::ref(mi));
    std::thread t2(setNum, std::ref(mi), 100);
    std::thread t3(printNum, std::ref(mi));
    std::thread t4(printNum, std::ref(mi));
    
    t1.join();
    t2.join();
    t3.join();
    t4.join();

    std::cout << "final num : " << mi.num << std::endl;

    return 0;
}

'C++ > Thread' 카테고리의 다른 글

[Thread] scoped static 초기화  (0) 2022.07.19
[Thread] std::call_once  (0) 2022.07.18
[Thread] Deadlock, std::scoped_lock  (0) 2022.07.17
[Thread] std::unique_lock  (0) 2022.07.17
[Thread] std::mutex  (0) 2022.07.15
    'C++/Thread' 카테고리의 다른 글
    • [Thread] scoped static 초기화
    • [Thread] std::call_once
    • [Thread] Deadlock, std::scoped_lock
    • [Thread] std::unique_lock
    CodeNote
    CodeNote
    기록 블로그

    티스토리툴바