锁在多线程编程中被广泛使用,通过锁能够保证临界区的数据顺序访问。在使用锁的过程中,最容易现的问题是:死锁。幸好C++为我们提供了很好的机制尽量避免死锁的出现。
std::lock_guard 是一个RAII风格的锁,在离开作用域范围时,自动释放锁。下面直接看一个例子:
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include <cassert>
static int g_index = 0;
static const int CNT = 1000;
std::mutex g_mutex;
static void _inc(void) {
for (int i = 0; i < CNT; i++) {
const std::lock_guard<std::mutex> lock(g_mutex);
++g_index;
}
}
int main(void) {
std::vector<std::thread> v_th;
for (int i = 0; i < 10; i++) {
std::thread t(_inc);
v_th.push_back(std::move(t));
}
for (auto &t : v_th) {
t.join();
}
assert(g_index == 1000 * 10);
return 0;
}
std::unique_lock是一个功能更全的锁。支持延迟锁,时间锁,互斥锁,递归锁,还能支持转移锁的所有权等功能。下面也看一个例子:
#include <cassert>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
static const int CNT = 1000;
static int g_inc_index = 0;
static int g_dec_index = 0;
std::mutex g_inc_mutex;
std::mutex g_dec_mutex;
static void _run(void) {
for (int i = 0; i < CNT; i++) {
std::unique_lock<std::mutex> inc_lock(g_inc_mutex, std::defer_lock);
std::unique_lock<std::mutex> dec_lock(g_dec_mutex, std::defer_lock);
std::lock(inc_lock, dec_lock);
++g_inc_index;
--g_dec_index;
}
}
int main(void) {
std::vector<std::thread> v_th;
for (int i = 0; i < 10; i++) {
v_th.emplace_back(_run);
}
for (auto &t : v_th) {
t.join();
}
assert(g_inc_index == -g_dec_index);
return 0;
}
std::scoped_lock与std::lock_guard类似,不同的是能够同时获取多个锁。下面看一个例子:
#include <cassert>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
static int g_inc_index = 0;
static int g_dec_index = 0;
std::mutex g_inc_mutex;
std::mutex g_dec_mutex;
static void _run(const int count) {
for (int i = 0; i < count; i++) {
std::scoped_lock lock(g_inc_mutex, g_dec_mutex);
++g_inc_index;
--g_dec_index;
}
}
int main(void) {
std::vector<std::thread> v_th;
const int count = 1000;
for (int i = 0; i < 10; i++) {
v_th.emplace_back(_run, count);
}
for (auto &t : v_th) {
t.join();
}
assert(g_inc_index == -g_dec_index);
return 0;
}