Appearance
c++线程安全
涉及到的头包括
<mutex> <shared_mutex>
互斥量
std::mutex
c++中最基本的互斥量
cpp
#include <mutex>
#include <vector>
std::mutex mutex;
std::vector<int> vector;
void Set(int value) {
mutex.lock();
vector.push_back(value);
mutex.unlock();
}
std::vector<int> Get() {
mutex.lock();
std::vector<int> result = vector;
mutex.unlock();
}
#include <mutex>
#include <vector>
std::mutex mutex;
std::vector<int> vector;
void Set(int value) {
mutex.lock();
vector.push_back(value);
mutex.unlock();
}
std::vector<int> Get() {
mutex.lock();
std::vector<int> result = vector;
mutex.unlock();
}
std::recursive_mutex
可递归的互斥量
cpp
#include <mutex>
#include <vector>
std::recursive_mutex rmutex;
std::vector<int> vector;
bool Exist(int value) {
rmutex.lock()
for (int i : vector) {
if (i == value) {
rmutex.unlock();
return true;
}
}
rmutex.unlock();
return false;
}
void Set(int value) {
rmutex.lock();
if (Exist(value)) {
rmutex.unlock();
return;
}
vector.push_back(value);
rmutex.unlock();
}
std::vector<int> Get() {
rmutex.lock();
std::vector<int> result = vector;
rmutex.unlock();
}
#include <mutex>
#include <vector>
std::recursive_mutex rmutex;
std::vector<int> vector;
bool Exist(int value) {
rmutex.lock()
for (int i : vector) {
if (i == value) {
rmutex.unlock();
return true;
}
}
rmutex.unlock();
return false;
}
void Set(int value) {
rmutex.lock();
if (Exist(value)) {
rmutex.unlock();
return;
}
vector.push_back(value);
rmutex.unlock();
}
std::vector<int> Get() {
rmutex.lock();
std::vector<int> result = vector;
rmutex.unlock();
}
std::time_mutex 与 std::recursive_timed_mutex
带有超时功能的互斥量
cpp
#include <chrono>
#include <mutex>
#include <vector>
using namespace std::chrono_literals;
std::vector<int> vector;
std::timed_mutex tmutex;
void Set() {
if (tmutex.try_lock_for(3s)) {
// can lock, set something.
tmutex.unlock();
} else {
// can't lock, do nothing.
}
}
#include <chrono>
#include <mutex>
#include <vector>
using namespace std::chrono_literals;
std::vector<int> vector;
std::timed_mutex tmutex;
void Set() {
if (tmutex.try_lock_for(3s)) {
// can lock, set something.
tmutex.unlock();
} else {
// can't lock, do nothing.
}
}
std::shared_mutex 与std::shared_timed_mutex
从c++17起引入了std::shared_mutex,c++17之前想使用共享互斥量可以使用std::shared_timed_mutex
cpp
#include <shared_mutex>
#include <vector>
#if __cplusplus < 201703L
std::shared_timed_mutex mutex;
#else
std::shared_mutex mutex;
#endif
std::vector<int> vector;
void Set(int value) {
mutex.lock(); // 独占上锁
vector.push_back(value);
mutex.unlock();
}
std::vector<int> Get() {
mutex.lock_shared(); // 共享上锁
std::vector<int> result = vector;
mutex.unlock_shared();
return result;
}
#include <shared_mutex>
#include <vector>
#if __cplusplus < 201703L
std::shared_timed_mutex mutex;
#else
std::shared_mutex mutex;
#endif
std::vector<int> vector;
void Set(int value) {
mutex.lock(); // 独占上锁
vector.push_back(value);
mutex.unlock();
}
std::vector<int> Get() {
mutex.lock_shared(); // 共享上锁
std::vector<int> result = vector;
mutex.unlock_shared();
return result;
}
锁
手动操控互斥量很烦锁,而且很容易忘记解锁,所以标准库引入了”工具锁“来帮助程序员更方便的控制互斥量
std::lock_guard
守护锁,实现最为简单的锁,是一个“不可控”的锁,也就是说一旦锁上了,只能等待出作用域守护锁才能自动解锁,是性能最好的锁
cpp
#include <mutex>
#include <vector>
std::mutex mutex;
std::vector<int> vector;
void Set(int value) {
#if __cplusplus < 201703L
std::lock_guard<std::mutex> guard(mutex);
#else
std::lock_guard guard(mutex);
#endif
vector.push_back(value);
}
std::vector<int> Get() {
#if __cplusplus < 201703L
std::lock_guard<std::mutex> guard(mutex);
#else
std::lock_guard guard(mutex);
#endif
return vector;
}
#include <mutex>
#include <vector>
std::mutex mutex;
std::vector<int> vector;
void Set(int value) {
#if __cplusplus < 201703L
std::lock_guard<std::mutex> guard(mutex);
#else
std::lock_guard guard(mutex);
#endif
vector.push_back(value);
}
std::vector<int> Get() {
#if __cplusplus < 201703L
std::lock_guard<std::mutex> guard(mutex);
#else
std::lock_guard guard(mutex);
#endif
return vector;
}
std::unique_lock与std::shared_lock
独占锁与共享锁
与守护锁相比,独占锁是一个灵活的锁,但是与守护锁相比性能略差,可以通过lock()与unlock()方法来手动的控制加锁与解锁,如果程序员忘记解锁了,在出作用的时候独占锁也会自动的解锁
独占锁与共享锁在读写的场景下使用较多,也就是俗称的”读写锁“
独占锁与共享锁的特性:
在独占锁加锁时,在解锁前,所有对该互斥量请求加锁的线程都会被阻塞
在共享锁加锁时,如果其他线程对该互斥量请求共享加锁不会阻塞,而对该互斥量请求独占加锁将会阻塞
在共享锁加锁时,如果既有独占锁请求加锁也有共享锁请求加锁,那么独占锁请求后将会阻塞之后的共享锁的请求
读写锁例子:
cpp
#include <shared_mutex>
#include <vector>
#if __cplusplus < 201703L
std::shared_timed_mutex mutex;
#else
std::shared_mutex mutex;
#endif
std::vector<int> vector;
void Set(int value) {
#if __cplusplus < 201703L
std::unique_lock<std::shared_timed_mutex> lock(mutex);
#else
std::unique_lock lock(mutex);
#endif
vector.push_back(value);
}
std::vector<int> Get() {
#if __cplusplus < 201703L
std::shared_lock<std::shared_timed_mutex> lock(mutex);
#eles
std::shared_lock lock(mutex);
#endif
return vector;
}
#include <shared_mutex>
#include <vector>
#if __cplusplus < 201703L
std::shared_timed_mutex mutex;
#else
std::shared_mutex mutex;
#endif
std::vector<int> vector;
void Set(int value) {
#if __cplusplus < 201703L
std::unique_lock<std::shared_timed_mutex> lock(mutex);
#else
std::unique_lock lock(mutex);
#endif
vector.push_back(value);
}
std::vector<int> Get() {
#if __cplusplus < 201703L
std::shared_lock<std::shared_timed_mutex> lock(mutex);
#eles
std::shared_lock lock(mutex);
#endif
return vector;
}
unique_lock的例子:
cpp
#include <chrono>
#include <mutex>
#include <vector>
using namespace std::chrono_literals;
std::mutex mutex;
std::timed_mutex tmutex;
std::vector<int> vector;
void Set(int value) {
std::unique_lock lock(mutex);
vector.push_back(value);
lock.unlock();
// do something
lock.lock();
vector.push_back(value * value);
lock.unlock();
}
void SetWithTime(int value) {
std::unique_lock lock(tmutex, std::defer_lock_t); // 延迟加锁
if (lock.try_lock_for(3s)) {
// can lock, set something.
lock.unlock();
} else {
// can't lock, do nothing.
}
}
#include <chrono>
#include <mutex>
#include <vector>
using namespace std::chrono_literals;
std::mutex mutex;
std::timed_mutex tmutex;
std::vector<int> vector;
void Set(int value) {
std::unique_lock lock(mutex);
vector.push_back(value);
lock.unlock();
// do something
lock.lock();
vector.push_back(value * value);
lock.unlock();
}
void SetWithTime(int value) {
std::unique_lock lock(tmutex, std::defer_lock_t); // 延迟加锁
if (lock.try_lock_for(3s)) {
// can lock, set something.
lock.unlock();
} else {
// can't lock, do nothing.
}
}
std::scoped_lock
从c++17起引入范围锁,可以一次同时对多个不同的互斥量进行加锁,进一步防止死锁的发生
cpp
#include <mutex>
std::vector<int> a;
std::vector<int> b;
std::mutex amutex;
std::mutex bmutex;
void Set(int i, int j) {
std::scoped_lock lock(amutex, bmutex);
a.push_back(i);
b.push_back(j);
}
std::vector<int> GetA() {
std::lock_guard guard(amutex);
return a;
}
std::vector<int> GetB() {
std::lock_guard guard(bmutex);
return b;
}
#include <mutex>
std::vector<int> a;
std::vector<int> b;
std::mutex amutex;
std::mutex bmutex;
void Set(int i, int j) {
std::scoped_lock lock(amutex, bmutex);
a.push_back(i);
b.push_back(j);
}
std::vector<int> GetA() {
std::lock_guard guard(amutex);
return a;
}
std::vector<int> GetB() {
std::lock_guard guard(bmutex);
return b;
}
std::once_flag 与 std::call_once
在多线程中,有一些场景是某个任务只需要执行一次,可以用std::call_once函数配合std::once_flag标志量来实现
线程安全单例:
cpp
// container.h
class Container {
public:
static Container *GetInstance();
private:
Container() = default;
~Container() = default;
static inline Container *instance_ = nullptr;
};
// container.h
class Container {
public:
static Container *GetInstance();
private:
Container() = default;
~Container() = default;
static inline Container *instance_ = nullptr;
};
cpp
// container.cc
static std::once_flag instanceFlag;
Container *Container::GetInstance() {
std::call_once(instanceFlag, [] { instance_ = new Container(); });
return instance_;
}
// container.cc
static std::once_flag instanceFlag;
Container *Container::GetInstance() {
std::call_once(instanceFlag, [] { instance_ = new Container(); });
return instance_;
}