一、条件变量的概念
  • 条件变量是线程可用的另一种同步机制
  • 条件变量给多个线程提供了一个会合的场所
  • 条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生
  • 条件变量是线程中的东西,就是等待某一条件的发生,和信号一样
二、条件变量的使用
  • 条件变量要与互斥量一起使用,条件本身是由互斥量保护的。线程在改变条件状态之前必须首先锁住互斥量
  • 其他线程在获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定以后才能计算条件
三、条件变量数据类型(pthread_cond_t
  • pthread_cond_t
四、条件变量的初始化与释放

①静态初始化

  • 直接把pthread_cond_t定义的条件变量设置为常量PTHREAD_COND_INITIALIZER
  • 静态初始化条件变量只能拥有默认的条件变量属性,不能设置其他条件变量属性(条件变量属性见文章:javascript:void(0)
  • 例如:
pthread_cond_t cond;
cond=PTHREAD_COND_INITIALIZER;

//或者
pthread_cond_t *cond=(pthread_cond_t *)malloc(sizeof(pthread_cond_t));
*cond=PTHREAD_COND_INITIALIZER;

②动态初始化

  • 静态初始化条件变量只能拥有默认条件变量属性,我们可以通过pthread_mutex_init函数来动态初始化条件变量,并且可以在初始化时选择设置条件变量的属性
#include <pthread.h>
int pthread_cond_init(pthread_cond_t* restrict cond,const pthread_condattr_t* restrict attr);
int pthread_cond_destroy(pthread_cond_t* cond);

//返回值:成功返回0;失败返回错误编号

pthread_cond_init:

  • 功能:对条件变量初始化
  • 参数:
    • 参数1: 需要初始化的条件变量
    • 参数2:初始化时条件变量的属性。如果使用默认属性,此处填NULL(条件变量属性见文章:javascript:void(0)

pthread_cond_destroy:

  • 功能:对条件变量反初始化(在条件变量释放内存之前)
  • 参数:条件变量
  • 备注(重点):此函数只是反初始化互斥量,并没有释放内存空间,如果互斥量是通过malloc等函数申请的,那么需要在free掉互斥量之前调用pthread_mutex_destroy函数

例如:

pthread_cond_t cond;
pthread_cond_init(&cond,NULL);

/*do something*/

pthread_cond_destroy(&cond);
pthread_cond_t * cond=(pthread_cond_t *)malloc(sizeof(pthread_cond_t));
pthread_cond_init(cond,NULL);

/*do something*/

pthread_cond_destroy(cond);
free(cond);
五、等待条件变量函数
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t* restrict cond,pthread_mutex_t* restrict mutex);
int pthread_cond_timedwait(pthread_cond_t* cond,pthread_mutex_t* restrict mutex,const struct timespec* restrict tsptr);

//返回值:成功返回0;失败返回错误编号
  •  这两个函数调用成功返回时,线程需要重新计算条件,因为另一个线程可能已经在运行并改变了条件

pthread_cond_wait

  • 注意:等待条件变量变为真

  • 如何使用:参数mutex互斥量提前锁定,然后该互斥量对条件进行保护,等待参数1cond条件变量变为真。在等待条件变量变为真的过程中,此函数一直处于阻塞状态。但是处于阻塞状态的时候,mutex互斥量被解锁(因为其他线程需要使用到这个锁来使条件变量变为真)

  • 当pthread_cond_wait函数返回时,互斥量再次被锁住

pthread_cond_timedwait

  • pthread_cond_timedwait函数与pthread_cond_wait函数功能相同。不过多了一个超时参数。超时值指定了我们愿意等待多长时间,它是通过timespec结构体表示的
  • 如果超时到期之后,条件还是没有出现,此函数将重新获取互斥量,然后返回错误ETIMEOUT
  • 注意:这个时间值是一个绝对数而不是相对数。例如,假设愿意等待3分钟,那么不是把3分钟转换为timespec结构体,而是需要把当前时间加上3分钟再转换成timespec结构(可以查看互斥量的演示案例:javascript:void(0)

下面定义了一个得到超时值的绝对时间获取函数:

  • 可以使用clock_gettime函数获取timespec结构表示的当前时间。但是目前并不是所有的平台都支持这个函数。因此,可以使用gettimeofday获取timeval结构表示的当前时间,然后把这个时间转换为timespec结构
#include <sys/time.h>
#include <stdlib.h>

void maketimeout(struct timespec *tsp, long minutes)
{
	struct timeval now;

	/* get the current time */
	gettimeofday(&now, NULL);
	tsp->tv_sec = now.tv_sec;
	tsp->tv_nsec = now.tv_usec * 1000; /* usec to nsec */
	/* add the offset to get timeout value */
	tsp->tv_sec += minutes * 60;
}
六、条件变量信号发送函数
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);

//返回值:成功返回0;失败返回错误编号
  • 这两个函数用于通知线程条件变量已经满足条件(变为真)。在调用这两个函数时,是在给线程或者条件发信号
  • 必须注意:一定要在改变条件状态以后再给线程发信号
  • pthread_cond_signal函数:至少能唤醒一个等待该条件的线程
  • pthread_cond_broad函数:则唤醒等待该条件的所有线程
七、演示案例
  • 下面案例给出了如何结合使用条件变量和互斥量对线程进行同步
#include <pthread.h>

struct msg {
	struct msg *m_next;
	/* ... more stuff here ... */
};

struct msg *workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;

void  process_msg(void)
{
	struct msg *mp;

	for (;;) {
		pthread_mutex_lock(&qlock);
		while (workq == NULL)
			pthread_cond_wait(&qready, &qlock);
		mp = workq;
		workq = mp->m_next;
		pthread_mutex_unlock(&qlock);
		/* now process the message mp */
	}
}

void  enqueue_msg(struct msg *mp)
{
	pthread_mutex_lock(&qlock);
	mp->m_next = workq;
	workq = mp;
	pthread_mutex_unlock(&qlock);
	pthread_cond_signal(&qready);
}

APUE编程:62---线程处理(条件变量:pthread_cond_t)_互斥量

八、演示案例
  • 生产者生产数据,消费者消费数据
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> 
#include <strings.h>

#define BUFFSIZE 2
struct prodcons
{
    char buff[BUFFSIZE];
    int write_index;
    int read_index;
    pthread_cond_t notempty;
    pthread_cond_t notfull;
    pthread_mutex_t lock;
};

void  init(struct prodcons *pro);
void* producer_func(void* arg);
void* customer_func(void* arg);
void  put(struct prodcons *pro,int data);
int   get(struct prodcons *pro);

int main()
{
    pthread_t producer,customer;
    struct prodcons pro;

    init(&pro);
    
    if(pthread_create(&producer,NULL,producer_func,&pro)!=0){
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    if(pthread_create(&customer,NULL,customer_func,&pro)!=0){
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    if(pthread_join(producer,NULL)!=0){
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
    if(pthread_join(customer,NULL)!=0){
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }

    exit(0);
}

void init(struct prodcons *pro)
{
    bzero(pro->buff,sizeof(pro->buff));
    pro->write_index=0;
    pro->read_index=0;
    if((pthread_cond_init(&pro->notempty,NULL)!=0)
        ||(pthread_cond_init(&pro->notfull,NULL)!=0)
        ||(pthread_mutex_init(&pro->lock,NULL)!=0)){
        perror("init");
        exit(EXIT_FAILURE);
    }
}

void* producer_func(void* arg)
{
    int i;
    for(i=1;i<=5;++i)
    {
        printf("producer sleep 1 second to produce..\n");
        sleep(1);
        printf("put %d to produce\n",i);
        put(arg,i);
    }
    for(i=6;i<=10;++i)
    {
        printf("producer sleep 3 second to produce..\n");
        sleep(3);
        printf("produce:put %d\n",i);
        put(arg,i);
    }
    put(arg, -1);
    printf("producer exit\n");
    pthread_exit(0);
}

void* customer_func(void* arg)
{
    int data;
    while(1)
    {
        printf("customer wait 2 second to consumption.\n");
        sleep(2);
        data=get(arg);
        printf("customer:get %d\n",data);
        if(data==-1)
            break;
    }
    printf("customer exit\n");
    pthread_exit(0);
}

void put(struct prodcons *pro,int data)
{
    pthread_mutex_lock(&pro->lock);
    //如果满了,等到非满条件变量产生
    while(((pro->write_index+1)%BUFFSIZE) == pro->read_index)
    {
        printf("producer is full,wait not full");
        pthread_cond_wait(&pro->notfull,&pro->lock);
    }
    pro->buff[pro->write_index]=data;
    pro->write_index++;
    if(pro->write_index>=BUFFSIZE)
        pro->write_index=0;
    pthread_cond_signal(&pro->notempty);
    pthread_mutex_unlock(&pro->lock);
}

int   get(struct prodcons *pro)
{
    int data;
    pthread_mutex_lock(&pro->lock);
    //如果空了,则等待非空
    while(pro->write_index==pro->read_index)
    {
        printf("customer is empty,wait not empty\n");
        pthread_cond_wait(&pro->notempty,&pro->lock);
    }
    data=pro->buff[pro->read_index];
    pro->read_index++;
    if(pro->read_index>=BUFFSIZE)
        pro->read_index=0;
    pthread_cond_signal(&pro->notfull);
    pthread_mutex_unlock(&pro->lock);
    return data;
}
  • 演示结果较长,未显示完整

APUE编程:62---线程处理(条件变量:pthread_cond_t)_#include_02