std::lock_guard
template< class Mutex > | ||
类 lock_guard
是互斥封装器,为在作用域块期间占有互斥提供便利 RAII 风格机制。
创建 lock_guard
对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard
对象的作用域时,销毁 lock_guard
并释放互斥。
lock_guard
类不可复制。
#include <thread>
#include <mutex>
#include <iostream>
int g_i = 0;
std::mutex g_i_mutex; // 保护 g_i
void safe_increment()
{
std::lock_guard<std::mutex> lock(g_i_mutex);
++g_i;
std::cout << std::this_thread::get_id() << ": " << g_i << '\n';
// g_i_mutex 在锁离开作用域时自动释放
}
int main()
{
std::cout << "main: " << g_i << '\n';
std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join();
t2.join();
std::cout << "main: " << g_i << '\n';
}
//可能输出:
main:0
11452:1
12504:2
main:2
std::unique_lock
类 unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。
类 unique_lock 可移动,但不可复制——它满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 。
类 unique_lock 满足基础可锁 (BasicLockable) 要求。若 Mutex 满足可锁 (Lockable) 要求,则 unique_lock 亦满足可锁(Lockable) 要求(例如:能用于 std::lock ) ;若 Mutex 满足定时可锁 (TimedLockable) 要求,则 unique_lock 亦满足定时可锁 (TimedLockable) 要求。
std::unique_lock::lock
//锁定关联互斥 。 等效地调用mutex()->lock()
#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
/*下列示例用 lock 重获得被解锁的互斥。*/
int main()
{
int counter = 0;
std::mutex counter_mutex;
std::vector<std::thread> threads;
auto worker_task = [&](int id) {
std::unique_lock<std::mutex> lock(counter_mutex);
++counter;
std::cout << id << ", initial counter: " << counter << '\n';
lock.unlock();
// 我们模拟昂贵操作时不保有锁
std::this_thread::sleep_for(std::chrono::seconds(1));
lock.lock();
++counter;
std::cout << id << ", final counter: " << counter << '\n';
};
for (int i = 0; i < 10; ++i) threads.emplace_back(worker_task, i);
for (auto &thread : threads) thread.join();
}
//可能输出:
0, initial counter: 1
1, initial counter: 2
2, initial counter: 3
3, initial counter: 4
4, initial counter: 5
5, initial counter: 6
6, initial counter: 7
7, initial counter: 8
8, initial counter: 9
9, initial counter: 10
6, final counter: 11
3, final counter: 12
4, final counter: 13
2, final counter: 14
5, final counter: 15
0, final counter: 16
1, final counter: 17
7, final counter: 18
9, final counter: 19
8, final counter: 20
std::unique_lock::try_lock
bool try_lock();
尝试锁定关联互斥而不阻塞。等效地调用 mutex()->try_lock() 。
若无关联互斥或关联互斥已被此 std::unique_lock
锁定则抛出 std::system_error 。
std::unique_lock::try_lock_for
template< class Rep, class Period >
bool try_lock_for( const std::chrono::duration<Rep,Period>& timeout_duration );
尝试锁定关联互斥。阻塞直至经过指定的 timeout_duration
或获得锁,之先到来者。成功获得锁时返回 true ,否则返回 false 。等效地调用 mutex()->try_lock_for(timeout_duration) 。
由于调度或资源争议延迟,此函数可能阻塞长于 timeout_duration
。
标准建议用稳定时钟度量时长。若实现用系统时钟代替,则等待时间亦可能对时钟调节敏感。
若无关联互斥或若此 std::unique_lock
已锁定互斥则抛出 std::system_error 。
std::unique_lock::try_lock_until
template< class Clock, class Duration >
bool try_lock_until( const std::chrono::time_point<Clock,Duration>& timeout_time );
尝试锁定关联互斥。阻塞直至抵达指定的 timeout_time
或获得锁,之先到来者。成功获得锁时返回 true ,否则返回 false 。可能阻塞长于直至抵达 timeout_time
。
等效地调用 mutex()->try_lock_until(timeout_time) 。
若无关联互斥或已锁定互斥则抛出 std::system_error 。
std::unique_lock::unlock
void unlock();
解锁关联互斥并释放所有权。
若无关联互斥或互斥未被锁则抛出 std::system_error 。
std::defer_lock_t, std::try_to_lock_t, std::adopt_lock_t
struct defer_lock_t { };
struct try_to_lock_t { };
struct adopt_lock_t { };
std::defer_lock, std::try_to_lock, std::adopt_lock
std::defer_lock、
std::try_to_lock
和 std::adopt_lock
分别是空结构体标签类型 std::defer_lock_t 、 std::try_to_lock_t 和 std::adopt_lock_t 的实例。
#include <mutex>
#include <thread>
#include<iostream>
struct bank_account {
explicit bank_account(int balance) : balance(balance) {}
int balance;
std::mutex m;
};
void Transfer(bank_account &from, bank_account &to, int amount)
{
// 锁定两个互斥而不死锁
std::lock(from.m, to.m);
// 保证二个已锁定互斥在作用域结尾解锁
std::lock_guard<std::mutex> lock1(from.m, std::adopt_lock);
std::lock_guard<std::mutex> lock2(to.m, std::adopt_lock);
// 等价方法:
//std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
//std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
//std::lock(lock1, lock2);
std::cout << from.balance << ":" << to.balance << std::endl;
from.balance -= amount;
to.balance += amount;
std::cout << "Transfer " << amount << " from: " << from.balance
<< " to: " << to.balance << std::endl;
}
int main()
{
bank_account my_account(100);
bank_account your_account(50);
std::thread t1(Transfer, std::ref(my_account), std::ref(your_account), 20);
std::thread t2(Transfer, std::ref(your_account), std::ref(my_account), 5);
t1.join();
t2.join();
}
100:50
Transfer 20 from : 80 to : 70
70 : 80
Transfer 5 from : 65 to : 85