linux进阶52——pthread_cond_t

1. 概念

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

2. 使用场景

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

3. 数据类型

  • pthread_cond_t

4. 条件变量初始化与释放

①静态初始化

  • 直接把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);

5. 等待条件变量

#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;
}

6. 条件变量信号发送

#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函数:则唤醒等待该条件的所有线程

7. 示例

#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);
}

8. 案例

 

#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;
}
  • 16
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
pthread_cond_broadcast和pthread_cond_wait也是用于线程间同步的函数,类似于pthread_cond_signal和pthread_cond_wait的组合,但有一些区别。 pthread_cond_broadcast用于广播条件变量的信号。当一个线程调用pthread_cond_broadcast时,它会唤醒所有正在等待这个条件变量的线程。这与pthread_cond_signal的区别在于,pthread_cond_signal只会唤醒一个等待线程,而pthread_cond_broadcast会唤醒所有等待线程。 pthread_cond_wait用于等待条件变量的信号,与pthread_cond_signal和pthread_cond_broadcast一起使用。当一个线程调用pthread_cond_wait时,它会阻塞等待条件变量的信号。当收到信号后,线程会重新激活,并且会重新检查条件是否满足。如果条件不满足,线程可能会再次进入等待状态。 以下是一个使用pthread_cond_broadcast和pthread_cond_wait的示例代码: ```c pthread_mutex_t mutex; pthread_cond_t cond; int condition = 0; void* thread1(void* arg) { pthread_mutex_lock(&mutex); while (condition == 0) { pthread_cond_wait(&cond, &mutex); } // 条件满足后执行的代码 pthread_mutex_unlock(&mutex); return NULL; } void* thread2(void* arg) { pthread_mutex_lock(&mutex); condition = 1; pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t tid1, tid2; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); pthread_create(&tid1, NULL, thread1, NULL); pthread_create(&tid2, NULL, thread2, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; } ``` 在上述示例中,thread1线程调用pthread_cond_wait等待条件满足,而thread2线程在某个时刻将条件设置为满足,并调用pthread_cond_broadcast发送信号。这样,所有等待的线程都会被唤醒并执行相应的代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值