并不是给函数加锁就能完全保证线程安全,在设计接口时,不能将受保护的数据的指针、引用,传到互斥元范围以外,这将破坏互斥元的保护性。

即,加锁的函数不能返回受保护的数据指针和引用。

 

1 std::mutex

通过创建std::mutex的实例创建互斥元,调用lock()来锁定,调用unlock()来解锁。
但这种方式容易出问题,如果忘记了调用unlock(),就会导致程序阻塞。

2 lock_guard

为了解决这一问题,标准库提供了std::lock_guard类模板,实现了互斥元的RAII用法。
构造时,锁定互斥元,析构时(离开作用域)被正确解锁。
定义方式:

std::mutex mutex;
std::list<int> some_list;
void add_to_list(int a)
{
std::lock_guard<std::mutex> guard(mutex); //实现加锁和解锁
some_list.push_back(a);
}

3 unique_lock

相比于lock_guard,unique_lock并不是一创建就锁定,而是可以选择延迟锁定。
unique_lock内部存储了一个标识,通过该标识判断是否上锁和解锁。
因此,unique_lock对象比lock_guard大,且会有些许性能损失。

void swap(X& lhs. X& rhs)
{
if(&lhs == &rhs )
return;
std::unique_lock<std::mutex> lock_a(lhs.m, std::defer_lock); //延迟锁定
std::unique_lock<std::mutex> lock_b(lhs.m, std::defer_lock); //延迟锁定

std::lock(lock_a, lock_b); //锁定

swap(lhs.some, rhs.some);
}

unique_lock可以通过std::move转移所有权

 

posted on 2021-04-06 14:20  望月又一  阅读(140)  评论(0编辑  收藏  举报