c++标准提供的保护共享数据的最基本机制是互斥元(mutex)。
std::mutex my_mutex;
void fun() {
my_mutex.lock(); //互斥元加锁
do_something();
my_mutex.unlock(); //互斥元解锁
}
void fun2() {
std::lock_guard<std::mutex> my_lock(my_mutex); //使用lock_guard进行包装,在构造时锁定互斥元,在析构时解锁互斥元
do_something();
}
一个线程安全栈的类定义:
#include <memory>
#include <mutex>
#include <stack>
struct empty_stack: std::exception
{
const char* what() const throw() {
return "empty stack!";
};
};
template<typename T>
class threadsafe_stack
{
private:
std::stack<T>data_;
mutable MutexLock lock_;
public:
threadsafe_stack():data_(std::stack<int>()) {}
threadsafe_stack()(const threadsafe_stack& other) {
LockGuard<MutexLock>(&other.lock_);
data_ = other.data_ ; //
}
threadsafe_stack & operator=(const threadsafe_stack&)= delete;
void push(T new_value) {
LockGuard<MutexLock> lock(&lock_);
data_.push(new_value);
}
std::shared_ptr<T> pop() {
LockGuard<MutexLock> lock (&lock_);
if (data_.empty()) throw empty_stack(); // 在调用pop 前, 检查栈是否为空
std:: shared_ptr <T> const res(std::make_shared<T>(data_.top()));//在修改栈前,分配出返回值
data_.pop();
return res;
}
void pop(T& value)
{
LockGuard<MutexLock> lock(&lock_);
if(data_.empty()) throw empty_stack();
value=data_.top();
data_.pop();
}
bool empty() const
{
LockGuard<MutexLock> lock(&lock_);
return data_.empty();
}
};
使用std::lock同时给多个互斥元加锁:
std::mutex m1;
std::mutex m2;
std::lock(m1,m2);
std::lock_guard<std::mutex> lock_a(m1,std::adopt_lock); //std::adopt_lock表明已上锁
std::lock_guard<std::mutex> lock_b(m2,std::adopt_lock);
避免死锁
- 避免嵌套锁
- 在持有锁的时候,避免调用用户提供的代码
- 以固定的顺序获取锁
- 使用锁层次
//一个简单的分层次互斥元
class hierarchical_mutex {
private:
std::mutex internal_mutex;
std::mutex data_mutex;
unsigned long const hierarchy_value;
// 保存上一次的层次值,该锁不能重入,所以只需要一个简单数据类型来保存
unsigned long previous_hierarchy_value;
// 当前层次值
static thread_local unsigned long this_thread_hierarchy_value;
void check_for_hierarchy_violation()
{
// 当前线程锁定了更低等级的或者是同等级的锁
if (this_thread_hierarchy_value <= hierarchy_value)
{
throw std::logic_error("mutex hierarchy violated");
}
}
void update_hierarchy_value()
{
// 保存当前层次值
previous_hierarchy_value = this_thread_hierarchy_value;
// 改变当前层次值
this_thread_hierarchy_value = hierarchy_value;
}
public:
explicit hierarchical_mutex(unsigned long value)
:hierarchy_value(value),previous_hierarchy_value(0){}
void lock()
{
check_for_hierarchy_violation();
internal_mutex.lock();
lock_guard<std::mutex> lock(data_mutex);
update_hierarchy_value();
}
void unlock()
{
internal_mutex.unlock();
lock_guard<std::mutex> lock(data_mutex);
this_thread_hierarchy_value = previous_hierarchy_value;
}
bool try_lock()
{
check_for_hierarchy_violation();
if (!internal_mutex.try_lock())
return false;
lock_guard<std::mutex> lock(data_mutex);
update_hierarchy_value();
return true;
}
};
thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value = ULONG_MAX;
使用std::unique_lock灵活锁定
std::unique_lock同std::lock_guard功能类似但它更加灵活 。
它在lock_guard的基础上还能:
—— 没有关联互斥体时创建
—— 没有锁定的互斥体时创建
—— 显式和重复设置或释放关联互斥锁
—— 移动互斥体 move
—— 尝试锁定互斥体
—— 延迟锁定关联互斥体
std::mutex m;
std::unqiue_lock<std::mutex> my_lock(m,std::defer_lock); //使用std::defer_lock保留互斥元未锁定
my_lock.lock(); //加锁
my_lock.unlock(); //解锁
使用std::call_once使函数只被调用一次
std::once_flag f1;
void fun() {
std::cout << "hello call_once" << std::endl;
}
void fun_once() {
std::call_once(f1, fun);
}
int main() {
std::vector<std::thread> t;
for (int i = 0; i < 10; i++) {
//t.push_back(std::thread(fun));
t.push_back(std::thread(fun_once)); //只调用一次
t[i].join();
}
system("pause");
return 0;
}
读写锁:允许同一时刻对共享区域执行多个“读”操作,或者一个“写”操作,c++标准库并未给出实现
递归锁:可以多次lock,使用std::recursive_mutex(类似mutex用法)