新的C++版本提供了以下锁:
|
|
- 上述有timed字面值的锁对应没有timed字面值的锁多了两种操作(1. 可以指定请求锁等待的超时时间;2. 可以指定请求锁一直到某一个时刻)
- 超时时间类型为std::chrono::duration
- 时刻时间类型为std::chrono::time_point
- lock_guard、unique_lock、scoped_lock、std::lock、std::try_lock 可使用 全部的锁
- shared_lock 可使用 shared_mutex、shared_timed_mutex
- mutex、timed_mutex不可循环锁
- recursive_mutex、recursive_timed_mutex可循环锁
- shared_mutex、shared_timed_mutex为读写锁
提供了以下范围锁:
(C++11) | (C++11) |
(C++14) | (C++17) |
- lock_guard范围锁,简单,只有上锁和解锁两个操作,构造函数上锁,析构函数解锁
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <thread>
using std::cin;
using std::cout;
using std::endl;
template <class T>
void lock_guard(){
int i = 0;
T lock;
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::lock_guard<T> autolock(lock);
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
int main(int argc, char* argv[])
{
::lock_guard<std::mutex>();
::lock_guard<std::recursive_mutex>();
::lock_guard<std::timed_mutex>();
}
- unique_lock范围锁,功能多一些,提供上锁,解锁,尝试取得锁,超时请求锁,同时也用来做写锁
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <thread>
using std::cin;
using std::cout;
using std::endl;
template <class T>
void unique_lock(){
int i = 0;
T lock;
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::unique_lock<T> autolock(lock);
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
template <class T>
void unique_lock_after_time() {
int i = 0;
T lock;
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::unique_lock<T> autolock(lock, std::chrono::milliseconds(1'000));
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
template <class T>
void unique_lock_until_time() {
int i = 0;
T lock;
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::unique_lock<T> autolock(lock, std::chrono::time_point < std::chrono::high_resolution_clock >(std::chrono::milliseconds(1'000)));
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
int main(int argc, char* argv[])
{
// 上锁和解锁操作,一直等待到获得锁
::unique_lock<std::mutex>();
::unique_lock<std::recursive_mutex>();
::unique_lock<std::timed_mutex>();
::unique_lock<std::recursive_timed_mutex>();
::unique_lock<std::shared_mutex>();
::unique_lock<std::shared_timed_mutex>();
// 上锁和解锁操作,在一定时间内等待锁,否则超时
::unique_lock_after_time<std::timed_mutex>();
::unique_lock_after_time<std::recursive_timed_mutex>();
::unique_lock_after_time<std::shared_timed_mutex>();
// 上锁和解锁操作,等待获得锁一直到某一个时间点,否则超时
::unique_lock_until_time<std::timed_mutex>();
::unique_lock_until_time<std::recursive_timed_mutex>();
::unique_lock_until_time<std::shared_timed_mutex>();
}
- scoped_lock范围锁,可同时锁住多个锁
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <thread>
using std::cin;
using std::cout;
using std::endl;
template <class T>
void scoped_lock(){
int i = 0;
T lock;
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::scoped_lock<T> autolock(lock);
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
template <class T, class U>
void scoped_lock(){
int i = 0;
T lock;
U lock2;
auto fWThread = [&i, &lock, &lock2]() {
for (int k = 0; k < 10; k++) {
std::scoped_lock<T, U> autolock(lock, lock2);
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
template <class T, class U, class V>
void scoped_lock(){
int i = 0;
T lock;
U lock2;
V lock3;
auto fWThread = [&i, &lock, &lock2, &lock3]() {
for (int k = 0; k < 10; k++) {
std::scoped_lock<T, U, V> autolock(lock, lock2, lock3);
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
int main(int argc, char* argv[])
{
// 上锁和解锁操作,一直等待到获得锁
::scoped_lock<std::mutex>();
::scoped_lock<std::recursive_mutex>();
::scoped_lock<std::timed_mutex>();
// 上锁和解锁,同时对两个锁进行操作
::scoped_lock<std::mutex, std::mutex>();
::scoped_lock<std::mutex, std::recursive_mutex>();
// 上锁和解锁,同时对三个锁进行操作
::scoped_lock<std::mutex, std::mutex, std::mutex>();
::scoped_lock<std::mutex, std::recursive_mutex, std::timed_mutex>();
// 同理,上锁和解锁,可同时对多个锁进行操作
}
- shared_lock范围锁,读锁,读锁之间无互斥关系,不需要等待。常与unique_lock(作写锁)配合使用。
- 只能用于shared_mutex和shared_timed_mutex
- shared_lock只有在没有写锁的情况下才能获得锁,unique_lock只有在没有读锁的情况下才能获得锁
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <thread>
using std::cin;
using std::cout;
using std::endl;
template <class T>
void share_lock() {
int i = 0;
T lock;
auto fRThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::shared_lock<T> autolock(lock);
int j = i;
std::cout << std::this_thread::get_id() << " read: " << j << endl;
}
};
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::unique_lock<T> autolock(lock);
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread r1(fRThread);
std::thread w1(fWThread);
std::thread r2(fRThread);
r1.join();
r2.join();
w1.join();
}
template <class T>
void share_lock_until_time() {
int i = 0;
T lock;
auto fRThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::shared_lock<T> autolock(lock, std::chrono::time_point < std::chrono::high_resolution_clock >(std::chrono::milliseconds(1'000)));
int j = i;
std::cout << std::this_thread::get_id() << " read: " << j << endl;
}
};
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::unique_lock<T> autolock(lock, std::chrono::time_point < std::chrono::high_resolution_clock >(std::chrono::milliseconds(1'000)));
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread r1(fRThread);
std::thread w1(fWThread);
std::thread r2(fRThread);
r1.join();
r2.join();
w1.join();
}
template <class T>
void share_lock_after_time() {
int i = 0;
T lock;
auto fRThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::shared_lock<T> autolock(lock, std::chrono::milliseconds(1'000));
int j = i;
std::cout << std::this_thread::get_id() << " read: " << j << endl;
}
};
auto fWThread = [&i, &lock]() {
for (int k = 0; k < 10; k++) {
std::unique_lock<T> autolock(lock, std::chrono::milliseconds(1'000));
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread r1(fRThread);
std::thread w1(fWThread);
std::thread r2(fRThread);
r1.join();
r2.join();
w1.join();
}
int main(int argc, char* argv[])
{
// 上锁和解锁操作,一直等待到获得锁
::share_lock<std::shared_mutex>();
// 上锁和解锁操作,在一定时间内等待锁,否则超时
::share_lock_after_time<std::shared_timed_mutex>();
// 上锁和解锁操作,等待获得锁一直到某一个时间点,否则超时
::share_lock_until_time<std::shared_timed_mutex>();
}
提供了以下同时锁多个锁(不造成死锁):
(C++11) | (C++11) |
std::lock: 同时给多个锁上锁
std::try_lock:尝试同时给多个锁上锁
注意:这两个方法并不会去释放锁,需要自己主动去释放
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <thread>
using std::cin;
using std::cout;
using std::endl;
template <class T, class U>
void lock(){
int i = 0;
T lock;
T lock2;
U lock3;
auto fWThread = [&i, &lock, &lock2, &lock3]() {
for (int k = 0; k < 10; k++) {
std::lock(lock, lock2, lock3);
std::lock_guard<T> l1(lock, std::adopt_lock);// make sure lock is unlock finally
std::lock_guard<T> l2(lock2, std::adopt_lock);// make sure lock2 is unlock finally
std::lock_guard<U> l3(lock3, std::adopt_lock);// make sure lock3 is unlock finally
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
template <class T, class U>
void try_lock(){
int i = 0;
T lock;
T lock2;
U lock3;
auto fWThread = [&i, &lock, &lock2, &lock3]() {
for (int k = 0; k < 10; k++) {
// -1 means success
if (-1 == std::try_lock(lock, lock2, lock3)){
int j = ++i;
std::cout << std::this_thread::get_id() << " write: " << j << endl;
lock.unlock();
lock2.unlock();
lock3.unlock();
}
else{
std::cout << std::this_thread::get_id() << " get lock failed" << endl;
}
}
};
std::thread w1(fWThread);
std::thread w2(fWThread);
w1.join();
w2.join();
}
int main(int argc, char* argv[])
{
::lock<std::mutex, std::recursive_mutex>();
::try_lock<std::mutex, std::recursive_mutex>();
}
提供了以下结构体指明要对锁做的操作:
(C++11) | (C++11) | (C++11) |
以下分别是上面的常量实例化
(C++11) | (C++11) | (C++11) |
它们用于指定std::lock_guard, std::unique_lock和std::shared_lock的锁定策略。
- std::defer_lock: 不进行上锁操作
- std::try_to_lock: 会尝试去获得锁,不一定会获得锁,会立即返回
- std::adopt_lock: 会认为锁已经是上锁状态了。
如:
std::mutex lock;
// 此处构造autolock时不会对lock上锁
std::unique_lock<std::mutex> autolock(lock, std::defer_lock);
// 此处构造autolock时会尝试去获得锁,会立即返回
std::unique_lock<std::mutex> autolock(lock, std::try_to_lock);
// 此处构造autolock时会假定lock已经上锁了
std::unique_lock<std::mutex> autolock(lock, std::adopt_lock);
还提供了整个程序中只调用一次的接口:
(C++11) | (C++11) |
once_flag用于call_once,once_flag标记是否被调用过。
· 若被调用的函数执行过了,那么这个标记就会标记为已调用;
· 若在调用的函数执行过程中抛出了异常,那么这个标记仍标记为未调用。
call_once指定只需要调用一次的函数入口
#include <iostream>
#include <thread>
#include <mutex>
std::once_flag flag1, flag2;
void simple_do_once()
{
std::call_once(flag1, [](){ std::cout << "Simple example: called once\n"; });
}
void may_throw_function(bool do_throw)
{
if (do_throw) {
std::cout << "throw: call_once will retry\n"; // this may appear more than once
throw std::exception();
}
std::cout << "Didn't throw, call_once will not attempt again\n"; // guaranteed once
}
void do_once(bool do_throw)
{
try {
std::call_once(flag2, may_throw_function, do_throw);
}
catch (...) {
}
}
int main()
{
std::thread st1(simple_do_once);
std::thread st2(simple_do_once);
std::thread st3(simple_do_once);
std::thread st4(simple_do_once);
st1.join();
st2.join();
st3.join();
st4.join();
std::thread t1(do_once, true);
std::thread t2(do_once, true);
std::thread t3(do_once, false);
std::thread t4(do_once, true);
t1.join();
t2.join();
t3.join();
t4.join();
}
/*
Simple example: called once
throw: call_once will retry
throw: call_once will retry
Didn't throw, call_once will not attempt again
*/