互斥量、锁和条件变量
23 Nov 20211 lock_guard<mutex> lock
1.1 直接操作mutex的lock/unlock函数
多线程访问同一资源时,为了保证数据的一致性,必要时需要加锁mutex,表示任一时刻,只能有一个线程访问该对象,加锁mutex
#include <iostream>
#include <boost>
std::mutex m_buf;
void Counter(){
m_buf.lock();
int i=++count;// 只有当一个线程输出完毕了,另一个线程才能输出
m_buf.unlock();
}
int main(){
boost::thread_group threads;// 创建进程
for(int i=0;i<4;i++)
threads.create_thread(&Counter);
threads.join_all();
return 0;
}
1.2 使用 lock_guard/unique_lock 自动加锁、解锁
类似智能指针,与 1.1 相比只需要加锁,后面无须解锁 unlock()
#include <iostream>
#include <boost/thread/lock_guard.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
boost::mutex mutex;
int count = 0;
void Counter() {
// lock_guard 在构造函数里加锁,在析构函数里解锁。
boost::lock_guard<boost::mutex> lck(mutex);
int i = ++count;
std::cout << "count == " << i << std::endl;
}
int main() {
boost::thread_group threads;
for (int i = 0; i < 4; ++i) {
threads.create_thread(&Counter);
}
threads.join_all();
return 0;
}
unique_lock<mutex> lck(m);
就是创建了一个unique_lock对象lck,并将其与互斥量mutex绑定,同时对其上锁。
1.3 unique_lock 和 lock_guard 区别
- unique_lock不需要始终拥有关联的mutex,而lock_guard始终拥有mutex。这意味着unique_lock需要利用owns_lock()判断是否拥有mutex。
- unique_lock可以进行临时解锁和再上锁,如在构造对象之后使用lck.unlock()就可以进行解锁,lck.lock()进行上锁,而不必等到析构时自动解锁。
- unique_lock还接受第二个参数来进行构造。unique_lock
lck(m,perd):仅仅是将lck与m绑定,不会自动进行lock()和unlock(),需要配合条件变量使用。
如果要结合使用条件变量 condition_variable,应该使用 unique_lock
2 条件变量 condition_variable
条件变量主要包括两个函数 wait() 和 notify_one/all()
void wait(unique_lock<mutex>& _Lck)
{ // wait for signal
_Cnd_waitX(_Mycnd(), _Lck.mutex()->_Mymtx());
}
template<class _Predicate>
void wait(unique_lock<mutex>& _Lck, _Predicate _Pred)
{ // wait for signal and test predicate
while (!_Pred())
wait(_Lck);
}
函数有两个参数,程序执行到 wait() 语句时:
- 对互斥量加锁。
- 执行第二个参数表示的函数 _Pred。
- 函数 _Pred 返回 false 时,对互斥量解锁,程序阻塞,等待其它线程调用 nofify_one() 将其唤醒后,回到步骤(1)。
- 函数 _Pred返回 true 时,程序继续执行。
函数只有一个参数,程序执行到 wait() 语句时阻塞(相当于第二参数表示的函数始终返回false),等待其它线程调用 nofify_one() 将其唤醒后,再继续执行(相当于第二参数表示的函数始终返回true)。