考虑互斥量的使用,最基本的代码是:
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4
5 std::mutex g_my_mutex;
6
7 std::lock_guard<std::mutex> make_lock() {
8 return std::lock_guard<std::mutex>(g_my_mutex);
9 }
10
11 void workOnResource1() {
12
13 for (int i = 0; i < 10000; ++i)
14 {
15 std::lock_guard<std::mutex> lk(g_my_mutex);
16 --gi;
17 }
18 }
19
20 void workOnResource2() {
21
22 for (int i = 0; i < 10000; ++i)
23 {
24 auto lk = make_lock();
25 ++gi;
26 }
27 }
28
29 int main() {
30
31 std::thread t1(workOnResource1);
32 std::thread t2(workOnResource2);
33
34 t1.join();
35 t2.join();
36
37 std::cout << "gi=" <<gi;
38 }
这在很多例子里都常见。等等,为什么第8行编译不过去?那是因为您没有在C++17下编译。std::lock_guard是禁止拷贝和移动的。C++17 granteed copy ellision允许第8行编译通过。
比较一下,第24行和第15行,哪个更简洁一些呢?
std::lock_guard还可以这样用:
void workOnResource1() {
for (int i = 0; i < 10000; ++i)
{
g_my_mutex.lock();
std::lock_guard<std::mutex> lk(g_my_mutex, std::adopt_lock);
--gi;
}
}
意思就是,当lk对象构造时,不去调用mutex::lock()。因为之前自己所持有的锁已经lock了一次了(g_my_mutex.lock())。adopt就是这个意思的表达,英文的含义是收养。
当lk析构时,对g_my_mutex调用mutex::unlock,这一点没有变化。
现在考虑unique_lock的用法:用于更复杂的互斥量操作上,例如:有超时时间的加锁。
#include <iostream>
#include <thread>
#include <mutex> // std::mutex, std::lock_guard
#include <chrono>
using Ms = std::chrono::milliseconds;
using namespace std;
int gi = 0;
std::timed_mutex g_my_mutex;
std::lock_guard<std::timed_mutex> make_lock() {
return std::lock_guard<std::timed_mutex>(g_my_mutex); //绝不能分行写
}
void workOnResource1() {
for (int i = 0; i < 10000; ++i)
{
auto lk = make_lock();
++gi;
}
}
std::unique_lock<std::timed_mutex> make_lock2() {
std::unique_lock<std::timed_mutex> lk(g_my_mutex, std::defer_lock);
return lk; //故意分行写
}
void workOnResource2() {
for (int i = 0; i < 10000; ++i)
{
auto lk = make_lock2();
while(lk.try_lock_for(Ms(100))==false){
std::cout << "lock fail. reason timeout. now retry...";
}
--gi;
}
}
int main() {
std::thread t1(workOnResource1);
std::thread t2(workOnResource2);
t1.join();
t2.join();
std::cout << "gi=" <<gi;
}
unique_lock支持move语义,这样它就能飞出{}之外了。像极了std::unique_ptr。同样的,它也是RAII的,当析构时,调用mutex::unlock().
std::defer_lock是个全局变量,类型是std::defer_lock_t。显然,它用于函数的重载解析时,选不同的函数执行的。defer_lock的意思是,暂时不对g_my_mutex调用任何加锁动作。
为了证明,unique_lock是可以move的,可以这样:
std::unique_lock<std::timed_mutex> lk(g_my_mutex, std::defer_lock); std::unique_lock<std::timed_mutex> lk2(std::move(lk)); lk2.lock();
scoped_lock是C++17新引进的,在处理多个互斥量时,特别简单:
参考:http://en.cppreference.com/w/cpp/thread/scoped_lock
菜鸟笔记