thread_lock_guard
thread_lock_guard (뮤텍스 자동 잠금 관리)
이 페이지에서는 C++ 표준 라이브러리의 lock_guard 클래스 템플릿을 설명해요. lock_guard는 뮤텍스를 소유하는 RAII 스타일의 래퍼로, 생성 시 뮤텍스를 잠그고 소멸 시 자동으로 해제해요. 이를 통해 예외가 발생하더라도 안전하게 뮤텍스를 해제할 수 있어요.
출처: cppreference
본문
정의
<mutex> 헤더에 정의됨 |
||
|---|---|---|
template < class Mutex > class lock_guard ; |
(since C++11) |
lock_guard 클래스는 뮤텍스 래퍼로, 범위 블록 동안 뮤텍스를 소유하기 위한 편리한 RAII 스타일 메커니즘을 제공해요.
lock_guard 객체가 생성되면 주어진 뮤텍스의 소유권을 가져오려고 시도해요. 제어가 lock_guard 객체가 생성된 범위를 벗어나면 lock_guard는 소멸되고 뮤텍스는 해제돼요.
lock_guard 클래스는 복사할 수 없어요.
템플릿 매개변수
| Mutex | - | 잠글 뮤텍스의 타입이에요. 이 타입은 BasicLockable 요구 사항을 충족해야 해요. |
|---|
멤버 타입
| 멤버 타입 | 정의 |
|---|---|
| mutex_type | Mutex |
멤버 함수
| (생성자) | lock_guard를 생성하고, 주어진 뮤텍스를 선택적으로 잠가요. (공개 멤버 함수) |
|---|---|
| (소멸자) | lock_guard 객체를 소멸시키고 내부 뮤텍스를 해제해요. (공개 멤버 함수) |
| operator= [deleted] | 복사 대입이 불가능해요. (공개 멤버 함수) |
주의 사항
초보자가 자주 하는 실수는 lock_guard 변수에 이름을 붙이는 것을 잊는 거예요. 예를 들어 std::lock_guard<std::mutex>(mtx);라고 쓰면 mtx라는 이름의 lock_guard 변수를 기본 생성하게 되고, std::lock_guard{mtx};라고 쓰면 즉시 파괴되는 prvalue 객체를 생성하게 돼요. 이렇게 하면 실제로 나머지 범위 동안 뮤텍스를 보유하는 잠금을 생성하지 못해요.
std::scoped_lock은 lock_guard의 대안으로, 교착 상태 방지 알고리즘을 사용해 여러 뮤텍스를 잠글 수 있는 기능을 제공해요. (since C++17)
예제
두 스레드가 volatile 변수를 안전하게 증가시키는 경우와 안전하지 않게 증가시키는 경우를 보여줘요.
#include <iostream>
#include <mutex>
#include <string_view>
#include <syncstream>
#include <thread>
volatile int g_i = 0;
std::mutex g_i_mutex; // protects g_i
void safe_increment(int iterations)
{
const std::lock_guard<std::mutex> lock(g_i_mutex);
while (iterations-- > 0)
g_i = g_i + 1;
std::cout << "thread #" << std::this_thread::get_id() << ", g_i: " << g_i << '\n';
// g_i_mutex is automatically released when lock goes out of scope
}
void unsafe_increment(int iterations)
{
while (iterations-- > 0)
g_i = g_i + 1;
std::osyncstream(std::cout) << "thread #" << std::this_thread::get_id()
<< ", g_i: " << g_i << '\n';
}
int main()
{
auto test = [](std::string_view fun_name, auto fun)
{
g_i = 0;
std::cout << fun_name << ":\nbefore, g_i: " << g_i << '\n';
{
std::jthread t1(fun, 1'000'000);
std::jthread t2(fun, 1'000'000);
}
std::cout << "after, g_i: " << g_i << "\n\n";
};
test("safe_increment", safe_increment);
test("unsafe_increment", unsafe_increment);
}
가능한 출력:
safe_increment:
before, g_i: 0
thread #140121493231360, g_i: 1000000
thread #140121484838656, g_i: 2000000
after, g_i: 2000000
unsafe_increment:
before, g_i: 0
thread #140121484838656, g_i: 1028945
thread #140121493231360, g_i: 1034337
after, g_i: 1034337
결함 보고서
다음 동작 변경 결함 보고서는 이전에 발표된 C++ 표준에 소급 적용되었어요.
| DR | 적용 대상 | 공개된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 2981 | C++17 | lock_guard<Mutex>에서 중복된 추론 가이드가 제공되었음 |
제거됨 |
같이 보기
| unique_lock (C++11) | 이동 가능한 뮤텍스 소유권 래퍼를 구현해요. (클래스 템플릿) |
|---|---|
| scoped_lock (C++17) | 여러 뮤텍스를 위한 교착 상태 회피 RAII 래퍼예요. (클래스 템플릿) |