并不是给函数加锁就能完全保证线程安全,在设计接口时,不能将受保护的数据的指针、引用,传到互斥元范围以外,这将破坏互斥元的保护性。
即,加锁的函数不能返回受保护的数据指针和引用。
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转移所有权
行万里路,不忘初心!