一、条件变量的概念

  • 条件变量是线程可用的​另一种同步机制
  • 条件变量​给多个线程​提供了一个会合的场所
  • 条件变量​与互斥量一起使用​时,允许线程以​无竞争的方式等待​特定的条件发生
  • 条件变量是线程中的东西,就是等待某一条件的发生,和信号一样

二、条件变量的使用

  • 条件变量要与互斥量一起使用,条件本身是由互斥量保护的。线程在​改变条件状态之前​必须首​先锁住​互斥量
  • 其他线程在获得互斥量之前​不会察觉​到这种改变,因为互斥量必须在锁定以后才能计算条件

三、条件变量数据类型(pthread_cond_t)

  • pthread_cond_t

四、条件变量的初始化与释放


①静态初始化

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

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(条件变量属性见文章:​​​

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结构(可以查看互斥量的演示案例:



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

  • 可以使用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)_互斥量_02