1.Pthread条件变量简介
条件变量也是线程间同步一个重要的内容,如果说互斥是一个种竞争关系,那么条件变量用于协调线程之间的关系,是一种合作关系。
条件变量的应用很多,例如:BlockingQueue
,ThreadPool
等。
2. 条件变量的封装
其实就是对pthread_cond_t
和相关函数的封装:
#include <pthread.h> pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//静态初始化 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);//动态初始化 int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); int pthread_cond_destroy(pthread_cond_t *cond);
另外,我们在调用pthread_cond_wait的时候,必须先加锁,需要用到之前MutexLock
的私用成员函数,这里已经在MutexLock.h
中将class Condition
设定为了MutexLock
的一个友元。
//Condition.h #ifndef __CONDITION_H__ #define __CONDITION_H__ #include "MutexLock.h" #include <pthread.h> #include <boost/noncopyable.hpp> #include <assert.h> class MutexLock; class Condition { public: Condition(MutexLock &mutex); ~Condition(); void wait();//封装pthread_cond_wait void notify();//封装pthread_cond_signal void notifyAll();//封装pthread_cond_broadcast private: pthread_cond_t cond_; MutexLock &mutex_; }; #endi#include "Condition.#include "MutexLock.h"#include <assert.h>
Condition::Condition(MutexLock &mutex):mutex_(mutex) { CHECK(!pthread_cond_init(&cond_, NULL));//条件变量初始化 } Condition::~Condition() { CHECK(!pthread_cond_destroy(&cond_));//析构操作 } void Condition::wait() { assert(mutex_.isLocking());//wait前必须上锁 assert函数就是用来调试的 assert(exp) 若exp的表达式为假 则程序不执行结束报错 为真则什么也不做!! CHECK(!pthread_cond_wait(&cond_, mutex_.getMutexPtr())); //pthread_cond_wait 阻塞时释放锁,返回时会自动加锁 mutex_.restoreMutexStatus(); //还原状态 } void Condition::notify() { CHECK(!pthread_cond_signal(&cond_));//通知等待线程队列中的线程 } void Condition::notifyAll() { CHECK(!pthread_cond_broadcast(&cond_));//通知所有等待线程 }
注意学习下知识点:!!!
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,
其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。
mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),
而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。
在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个; 而pthread_cond_broadcast()则激活所有等待线程。
=========================================================
在网上看到一个名词“惊群效应”,意思就是说broadcast
使用不当,当唤醒所有线程而只有一个线程能够拿到资源,所以关于broadcast
还是要慎用。
关于封装条件变量的一般使用,假设我们要实现简单的容量无限的BlockingQueue
,可以这样写: BlockingQueue就是阻塞队列 经典到就是生产者消费者模型的实现
MutexLock mutex; Condition cond(mutex); std::deque<int> queue; int dequeue()//出列 { MutexLockGurad lock(mutex); while(queue.empty()) { cond.wait();//这一步会释放mutex并进入等待,这两个操作是原子的 //wait()返回后,会自动重新加锁 这个wait就是condition类中封装的 pthread_cond_wait函数 } assert(!queue.empty()); int top = queue.front(); queue.pop_front(); return top; } void enqueue(int x )//入队 { MutexLockGurad(mutex); queue.push_back(x); cond.notify();//这句也可以移出临界区 ??????不是特别明白 }
原文地址:https://www.cnblogs.com/zhangkele/p/9368110.html