scoped_lock
scoped_lock (스코프 잠금)
scoped_lock 클래스는 뮤텍스 래퍼로, 스코프 블록이 지속되는 동안 0개 이상의 뮤텍스를 소유할 수 있는 편리한 RAII 방식 메커니즘을 제공해요. 객체가 생성되면 주어진 뮤텍스들의 소유권을 획득하려 시도하고, 스코프를 벗어나면 소멸되면서 뮤텍스들을 해제해요. 여러 뮤텍스를 잠글 때는 std::lock과 동일한 교착상태 회피 알고리즘이 사용돼요.
출처: cppreference
본문
<mutex> 헤더에 정의됨 |
||
|---|---|---|
template < class ... MutexTypes > class scoped_lock ; |
(C++17 이후) |
scoped_lock 객체가 생성되면 전달받은 뮤텍스들의 소유권을 획득하려고 시도해요. 제어 흐름이 scoped_lock 객체가 생성된 스코프를 벗어나면 scoped_lock이 소멸되면서 뮤텍스들이 해제돼요. 여러 뮤텍스가 주어지면 std::lock을 사용한 것과 동일한 교착상태 회피 알고리즘이 적용돼요.
scoped_lock 클래스는 복사할 수 없어요.
템플릿 매개변수
| MutexTypes | - | 잠글 뮤텍스들의 타입이에요. sizeof...(MutexTypes) == 1이 아닌 한 타입들은 Lockable 요구 사항을 충족해야 해요. 그 경우 유일한 타입은 BasicLockable을 충족해야 해요. |
|---|
멤버 타입
| 멤버 타입 | 정의 |
|---|---|
mutex_type (조건부 존재) |
sizeof...(MutexTypes) == 1이면 멤버 타입 mutex_type은 MutexTypes...의 유일한 타입인 Mutex와 같아요. 그렇지 않으면 mutex_type 멤버는 없어요. |
멤버 함수
| (생성자) | scoped_lock을 생성하고, 선택적으로 주어진 뮤텍스들을 잠가요 (public member function) [edit] |
|---|---|
| (소멸자) | scoped_lock 객체를 소멸시키고 기본 뮤텍스들을 해제해요 (public member function) [edit] |
operator= [deleted] |
복사 대입할 수 없어요 (public member function) [edit] |
참고 사항
초보자가 자주 하는 실수는 scoped_lock 변수에 이름을 붙이는 것을 "잊는" 거예요. 예를 들어 std::scoped_lock(mtx);는 mtx라는 이름의 scoped_lock 변수를 기본 생성하고, std::scoped_lock{mtx};는 즉시 소멸되는 prvalue 객체를 생성해요. 이렇게 하면 실제로는 나머지 스코프 동안 뮤텍스를 보유하는 잠금을 생성하지 않아요.
| Feature-test 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_lib_scoped_lock |
201703L | (C++17) | std::scoped_lock |
예제
다음 예제는 std::scoped_lock을 사용하여 교착상태 없이 뮤텍스 쌍을 잠그는 RAII 방식 예제예요.
#include <chrono>
#include <functional>
#include <iostream>
#include <mutex>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>
using namespace std::chrono_literals;
struct Employee
{
std::vector<std::string> lunch_partners;
std::string id;
std::mutex m;
Employee(std::string id) : id(id) {}
std::string partners() const
{
std::string ret = "Employee " + id + " has lunch partners: ";
for (int count{}; const auto& partner : lunch_partners)
ret += (count++ ? ", " : "") + partner;
return ret;
}
};
void send_mail(Employee&, Employee&)
{
// Simulate a time-consuming messaging operation
std::this_thread::sleep_for(1s);
}
void assign_lunch_partner(Employee& e1, Employee& e2)
{
std::osyncstream synced_out(std::cout);
synced_out << e1.id << " and " << e2.id << " are waiting for locks" << std::endl;
{
// Use std::scoped_lock to acquire two locks without worrying about
// other calls to assign_lunch_partner deadlocking us
// and it also provides a convenient RAII-style mechanism
std::scoped_lock lock(e1.m, e2.m);
// Equivalent code 1 (using std::lock and std::lock_guard)
// std::lock(e1.m, e2.m);
// std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock);
// std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock);
// Equivalent code 2 (if unique_locks are needed, e.g. for condition variables)
// std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock);
// std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
// std::lock(lk1, lk2);
synced_out << e1.id << " and " << e2.id << " got locks" << std::endl;
e1.lunch_partners.push_back(e2.id);
e2.lunch_partners.push_back(e1.id);
}
send_mail(e1, e2);
send_mail(e2, e1);
}
int main()
{
Employee alice("Alice"), bob("Bob"), christina("Christina"), dave("Dave");
// Assign in parallel threads because mailing users about lunch assignments
// takes a long time
std::vector<std::thread> threads;
threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob));
threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob));
threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice));
threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob));
for (auto& thread : threads)
thread.join();
std::osyncstream(std::cout) << alice.partners() << '\n'
<< bob.partners() << '\n'
<< christina.partners() << '\n'
<< dave.partners() << '\n';
}
가능한 출력:
Alice and Bob are waiting for locks
Alice and Bob got locks
Christina and Bob are waiting for locks
Christina and Alice are waiting for locks
Dave and Bob are waiting for locks
Dave and Bob got locks
Christina and Alice got locks
Christina and Bob got locks
Employee Alice has lunch partners: Bob, Christina
Employee Bob has lunch partners: Alice, Dave, Christina
Employee Christina has lunch partners: Alice, Bob
Employee Dave has lunch partners: Bob
결함 보고서
다음 동작 변경 결함 보고서는 이전에 발표된 C++ 표준에 소급 적용되었어요.
| DR | 적용 대상 | 발표된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 2981 | C++17 | scoped_lock<MutexTypes...>에서 중복 deduction guide가 제공되었음 |
제거됨 |
같이 보기
unique_lock (C++11) |
이동 가능한 뮤텍스 소유권 래퍼를 구현해요 (class template) [edit] |
|---|---|
lock_guard (C++11) |
엄격한 스코프 기반 뮤텍스 소유권 래퍼를 구현해요 (class template) [edit] |