thread_shared_mutex

thread_shared_mutex (공유 뮤텍스)

shared_mutex는 여러 스레드가 공유 데이터를 동시에 접근하지 못하도록 보호하는 동기화 기본 요소예요. 일반 뮤텍스가 배타적 접근만 제공하는 것과 달리, shared_mutex는 공유(shared)와 배타적(exclusive) 두 가지 접근 수준을 제공해요. 이 페이지에서는 shared_mutex의 개념, 멤버 함수, 사용 예시를 설명할게요.

출처: cppreference

본문

<shared_mutex> 헤더에 정의되어 있으며, C++17부터 사용할 수 있어요. shared_mutex 클래스는 공유 데이터를 보호하는 동기화 기본 요소로, 여러 스레드가 동시에 접근하는 것을 막아줘요. 배타적 접근만 제공하는 다른 뮤텍스와 달리, shared_mutex는 두 가지 접근 수준을 가져요.

  • 공유(shared): 여러 스레드가 같은 뮤텍스의 소유권을 공유할 수 있어요.
  • 배타적(exclusive): 오직 한 스레드만 뮤텍스를 소유할 수 있어요.

한 스레드가 배타적 잠금(lock, try_lock)을 획득하면, 다른 스레드는 공유 잠금을 포함한 어떤 잠금도 획득할 수 없어요. 반대로 한 스레드가 공유 잠금(lock_shared, try_lock_shared)을 획득하면, 다른 스레드는 배타적 잠금을 획득할 수 없지만 공유 잠금은 획득할 수 있어요. 배타적 잠금이 어떤 스레드에도 획득되지 않은 경우에만 여러 스레드가 공유 잠금을 획득할 수 있어요. 하나의 스레드 내에서는 동시에 하나의 잠금(공유 또는 배타적)만 획득할 수 있어요.

공유 뮤텍스는 공유 데이터를 여러 스레드가 동시에 안전하게 읽을 수 있고, 다른 스레드가 읽거나 쓰지 않을 때만 한 스레드가 데이터를 쓸 수 있어야 하는 상황에서 특히 유용해요. shared_mutex 클래스는 SharedMutexStandardLayoutType의 모든 요구 사항을 충족해요.

멤버 타입

멤버 타입 정의
native_handle_type (선택*) 구현 정의(implementation-defined)

멤버 함수

(생성자) 뮤텍스를 생성해요 (공개 멤버 함수)
(소멸자) 뮤텍스를 소멸해요 (공개 멤버 함수)
operator= [deleted] 복사 대입이 불가능해요 (공개 멤버 함수)
배타적 잠금
lock 뮤텍스를 잠그고, 사용할 수 없으면 블록해요 (공개 멤버 함수)
try_lock 뮤텍스 잠금을 시도하고, 사용할 수 없으면 반환해요 (공개 멤버 함수)
unlock 뮤텍스 잠금을 해제해요 (공개 멤버 함수)
공유 잠금
lock_shared 공유 소유권을 위해 뮤텍스를 잠그고, 사용할 수 없으면 블록해요 (공개 멤버 함수)
try_lock_shared 공유 소유권을 위해 뮤텍스 잠금을 시도하고, 사용할 수 없으면 반환해요 (공개 멤버 함수)
unlock_shared 공유 소유권의 뮤텍스 잠금을 해제해요 (공개 멤버 함수)
네이티브 핸들
native_handle 구현 정의 네이티브 핸들 객체를 반환해요 (공개 멤버 함수)

예제

아래 출력은 단일 코어 머신에서 생성된 결과예요. thread1이 시작되면 첫 번째 루프에 진입해 increment()를 호출한 다음 get()을 호출해요. 그러나 반환된 값을 std::cout에 출력하기 전에 스케줄러가 thread1을 잠들게 하고 thread2를 깨워요. thread2는 세 번의 루프 반복을 한 번에 실행할 충분한 시간을 가지게 되죠. 다시 thread1로 돌아와서, 아직 첫 번째 루프 반복 중이므로 카운터 값의 로컬 복사본인 1std::cout에 출력하고 나머지 두 번의 루프 반복을 실행해요. 멀티 코어 머신에서는 어떤 스레드도 잠들지 않으므로 출력이 오름차순으로 나올 가능성이 더 높아요.

#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <syncstream>
#include <thread>

class ThreadSafeCounter
{
public:
    ThreadSafeCounter() = default;

    // Multiple threads/readers can read the counter's value at the same time.
    unsigned int get() const
    {
        std::shared_lock lock(mutex_);
        return value_;
    }

    // Only one thread/writer can increment/write the counter's value.
    void increment()
    {
        std::unique_lock lock(mutex_);
        ++value_;
    }

    // Only one thread/writer can reset/write the counter's value.
    void reset()
    {
        std::unique_lock lock(mutex_);
        value_ = 0;
    }

private:
    mutable std::shared_mutex mutex_;
    unsigned int value_{};
};

int main()
{
    ThreadSafeCounter counter;

    auto increment_and_print = [&counter]()
    {
        for (int i{}; i != 3; ++i)
        {
            counter.increment();
            std::osyncstream(std::cout)
                << std::this_thread::get_id() << ' ' << counter.get() << '\n';
        }
    };

    std::thread thread1(increment_and_print);
    std::thread thread2(increment_and_print);

    thread1.join();
    thread2.join();
}

가능한 출력:

123084176803584 2
123084176803584 3
123084176803584 4
123084185655040 1
123084185655040 5
123084185655040 6

같이 보기

shared_timed_mutex (C++14) 공유 상호 배제 기능을 제공하고 타임아웃이 있는 잠금을 구현해요 (클래스)
shared_lock (C++14) 이동 가능한 공유 뮤텍스 소유권 래퍼를 구현해요 (클래스 템플릿)
unique_lock (C++11) 이동 가능한 뮤텍스 소유권 래퍼를 구현해요 (클래스 템플릿)

더 알아보기 (Learn more)

cppreference