首页 > 技术文章 > c++ 封装线程库 1

zhangkele 2018-07-25 20:20 原文

1.Pthread条件变量简介

条件变量也是线程间同步一个重要的内容,如果说互斥是一个种竞争关系,那么条件变量用于协调线程之间的关系,是一种合作关系。

条件变量的应用很多,例如:BlockingQueueThreadPool等。 

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();//这句也可以移出临界区   ??????不是特别明白  
}

 

推荐阅读