pthread_cond_wait总和一个互斥锁结合使用。在调用pthread_cond_wait前要先获取锁。pthread_cond_wait函数执行时先自动释放指定的锁,然后等待条件变量的变化。在函数调用返回之前,自动将指定的互斥量重新锁住。
int pthread_cond_signal(pthread_cond_t * cond);
pthread_cond_signal通过条件变量cond发送消息,若多个消息在等待,它只唤醒一个。pthread_cond_broadcast可以唤醒所有。调用pthread_cond_signal后要立刻释放互斥锁,因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住,如果pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求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()则激活所有等待线程。
下面是另一处说明:给出了函数运行全过程。 为什么在唤醒线程后要重新mutex加锁?
了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。
In Thread1:
pthread_mutex_lock(&m_mutex);
pthread_cond_wait(&m_cond,&m_mutex);
pthread_mutex_unlock(&m_mutex);
In Thread2:
pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex);
为什么要与pthread_mutex 一起使用呢? 这是为了应对 线程1在调用pthread_cond_wait()但线程1还没有进入wait cond的状态的时候,此时线程2调用了 cond_singal 的情况。 如果不用mutex锁的话,这个cond_singal就丢失了。加了锁的情况是,线程2必须等到 mutex 被释放(也就是 pthread_cod_wait() 释放锁并进入wait_cond状态 ,此时线程2上锁) 的时候才能调用cond_singal.
pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之间,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是各有有缺点。
之间:
pthread_mutex_lock
xxxxxxx
pthread_cond_signal
pthread_mutex_unlock
缺点:在某下线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)然后又回到内核空间(因为cond_wait返回后会有原子加锁的 行为),所以一来一回会有性能的问题。但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。
所以在Linux中推荐使用这种模式。
之后:
pthread_mutex_lock
xxxxxxx
pthread_mutex_unlock
pthread_cond_signal
优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了
缺点:如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程),而这在上面的放中间的模式下是不会出现的。
其实,sigal唤醒线程,真正的都是在unlock之后,设想如果lock--->signal--->wait---->unlock.,会如何,在wait的时候,就会释放锁,则另外wait的线程,会在本线程wait之后,继续执行。将这种模式,封装出来一个monitor类如下:
template <class T, class P> class TC_Monitor { public: /** * 定义锁控制对象 */ typedef TC_LockT<TC_Monitor<T, P> > Lock; typedef TC_TryLockT<TC_Monitor<T, P> > TryLock; /** * 构造函数 */ TC_Monitor() : _nnotify(0) { } /** * 析够 */ virtual ~TC_Monitor() { } /** * 锁 */ void lock() const { _mutex.lock(); _nnotify = 0; } /** * 解锁, 根据上锁的次数通知 */ void unlock() const { notifyImpl(_nnotify); _mutex.unlock(); } /** * 尝试锁 * * @return bool */ bool tryLock() const { bool result = _mutex.tryLock(); if(result) { _nnotify = 0; } return result; } /** * 等待 */ void wait() const { notifyImpl(_nnotify); try { _cond.wait(_mutex); } catch(...) { _nnotify = 0; throw; } _nnotify = 0; } /** * 等时间 * @param millsecond * * @return bool, false:超时了, ture:有事件来了 */ bool timedWait(int millsecond) const { notifyImpl(_nnotify); bool rc; try { rc = _cond.timedWait(_mutex, millsecond); } catch(...) { _nnotify = 0; throw; } _nnotify = 0; return rc; } /** * 通知某一个线程醒来 * 调用该函数之前必须加锁, 在解锁的时候才真正通知 */ void notify() { if(_nnotify != -1) { ++_nnotify; } } /** * 通知所有的线程醒来 * 该函数调用前之必须加锁, 在解锁的时候才真正通知 * */ void notifyAll() { _nnotify = -1; } protected: /** * 通知实现 * @param nnotify */ void notifyImpl(int nnotify) const { if(nnotify != 0) { if(nnotify == -1) { _cond.broadcast(); return; } else { while(nnotify > 0) { _cond.signal(); --nnotify; } } } } private: // noncopyable TC_Monitor(const TC_Monitor&); void operator=(const TC_Monitor&); protected: mutable int _nnotify; mutable P _cond; T _mutex; }; /** * 普通线程锁 */ typedef TC_Monitor<TC_ThreadMutex, TC_ThreadCond> TC_ThreadLock; /** * 循环锁(一个线程可以加多次锁) */ typedef TC_Monitor<TC_ThreadRecMutex, TC_ThreadCond> TC_ThreadRecLock; }