[Linux]-条件变量

目录

【条件变量】

1.条件变量概述

2.条件变量的实现

3.条件变量的API

3.1条件变量的初始化

3.2条件变量的释放

3.3等待条件

3.4唤醒等待的条件变量

4.利用互斥锁和条件变量实现生产者和消费者的关系


【条件变量】

1.条件变量概述

之前我们了解过的死锁的一种情况,就是两个线程通过管道互相读取数据的时候,读的线程抢到了互斥锁,但是由于管道的读端带阻塞功能,会使读线程阻塞,造成互斥锁无法解锁,导致写端的线程也阻塞,直接造成了死锁的出现。

我们可以通过设置管道读端不阻塞来解决这个问题,但是管道读端如果不阻塞的话,很难读取到数据,那么我们就要使用条件变量来解决这个问题


条件变量简单的说,就是休眠当前任务,然后当需要的条件满足的时候,再次唤醒休眠的任务

通常来说,条件变量是和互斥锁一起使用的,也就是任务一因为在当前的任务中无法完成需要的条件,只能挂起当前任务,然后通过任务二来完成这个条件(通常就是操作线程之间共享的资源),当任务二完成条件后,会发送信号给休眠的任务一,休眠的任务一被唤醒然后向下执行。


2.条件变量的实现

这里我们以管道读写端阻塞造成的线程之间出现死锁来举例,如何通过条件变量让线程A无法完成的条件让线程B来完成

 实现步骤

当任务A一开始抢到互斥锁并且成功上锁后,由于公共资源中没有数据能够读取,所以只能等待条件满足(也就是有数据写入,但是由于任务A无法实现此功能,所以利用条件变量,休眠任务A,让任务B去执行),调用pthread_cond_wait,等待条件满足。pthread_cond_wait的内部其实就是解开了任务A当前的互斥锁,阻塞等待条件满足,然后让B成功上锁,等待B往公共资源写入数据(满足条件),然后B会发出信号通知休眠的任务A,解除A等待条件的阻塞,然后A再次阻塞与上互斥锁的,此时B解开互斥锁,让A重新上锁,然后A满足条件后成功执行,最后解除A互斥锁


3.条件变量的API

3.1条件变量的初始化

#include <pthread.h>

int pthread_cond_init(pthread_cond_t * cond,
           const pthread_condattr_t * attr);

功能

        初始化一个条件变量

参数

        cond:需要初始化的条件变量的地址

        attr:条件变量的属性,通常为NULL(设置默认)

        也可以静态初始化 :pthread_cond_t cond = PTHREAD_COND_INITIALIZER

返回值

        成功:0

        失败:非0错误码


3.2条件变量的释放

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

功能

        销毁一个条件变量

参数

        cond:条件变量的地址

返回值

        成功:0

        失败:非0错误码


3.3等待条件

在条件变量中,如果一个线程需要等待一个条件满足,那么就需要用到条件变量,会阻塞在这里直到条件满足才会解阻塞。

其实底层就是打开当前线程的锁,然后使用其他线程来完成达到满足条件变量,再利用其他线程发出信号给阻塞的当前线程,解阻塞在重新上锁。(这几步是原子操作,底层实现)

#include <pthread.h>

int pthread_cond_timedwait(pthread_cond_t *cond,
           pthread_mutex_t *mutex,
           const struct timespec *abstime);
int pthread_cond_wait(pthread_cond_t *cond,
           pthread_mutex_t * mutex);

功能

        timewait可以指定等待的时间

        wait会一直阻塞直到条件满足

参数

        cond:条件变量

        mutex:互斥锁

返回值

        成功:0

        失败:非0错误码


3.4唤醒等待的条件变量

调用pthread_cond_wait会导致线程一直阻塞在当前,那么当条件变量满足的时候,该怎么解除阻塞的条件变量呢,需要用到pthread_cond_signal或者pthread_cond_broadcast

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

功能

        唤醒阻塞的条件变量,pthread_cond_signal唤醒至少一个阻塞在线程上的条件变量,而pthread_cond_broadcast唤醒所有阻塞在条件变量上的线程

参数

        cond:条件变量

返回值

        成功:0

        失败:非0错误码


4.利用互斥锁和条件变量实现生产者和消费者的关系

假如有一个仓库,当消费者和生产者之前只能单独进去购买或者存放货物(互斥),当仓库没有货物的时候,只能让生产者进去放东西然后再让消费者购买(条件变量)

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

//定义一个全局变量表示商品数目
int num = 3;
//创建一把互斥锁
pthread_mutex_t mutex;
//创建一个条件变量
pthread_cond_t cond;



//生产者线程函数
void* creat_fun(void* arg)
{
    while(1)
    {

        printf("生产者正在生产产品.....\n");
        sleep(3);

        //上锁
        pthread_mutex_lock(&mutex);
        
        //假设生产者3s生成一个
        printf("生产者往仓库里面放了一个产品,放之前数目为%d\n",num);
        num++;

        //唤醒休眠线程
        pthread_cond_broadcast(&cond);
        
        printf("放之后数目为%d\n",num);

        //解锁
        pthread_mutex_unlock(&mutex);
   
    }

    return NULL;
}


//消费者线程创建
void* custmer1_fun(void* arg)
{
    while(1)
    {
        //消费者1
        if( 0 == strcmp( "消费者1" , (char*)arg ) )
        {

            //上锁
            pthread_mutex_lock(&mutex);

            /*条件变量创建,如果没有产品只能等待条件满足,
            消费者是没有能力创建产品的*/
            if( num == 0 )
            {
                printf("消费者1发现没有产品,等待生产\n");
                pthread_cond_wait(&cond,&mutex);
            }
            //即使存在消费者抢锁,也只有仓库里面有东西才能够购买
            if( num > 0 )
            {
                printf("消费者1进入仓库买产品,当前剩余%d个产品\n",num);
                num--;
                printf("消费者1购买完毕,当前剩余%d个产品\n",num); 
                printf("消费者1正在使用产品\n");           
            }    
            
            //解锁
            pthread_mutex_unlock(&mutex);

            //消费者使用产品需要3s
            
            sleep(3);
        }
    }
    return NULL; 
}
//消费者线程创建
void* custmer2_fun(void* arg)
{
    while(1)
    {

        //消费者2
        if( 0 == strcmp( "消费者2" , (char*)arg ) )
        {
 
            //上锁
            pthread_mutex_lock(&mutex);

            /*条件变量创建,如果没有产品只能等待条件满足,
            消费者是没有能力创建产品的*/
            if( num == 0 )
            {
                printf("消费者2发现没有产品,等待生产\n");
                pthread_cond_wait(&cond,&mutex);
            }
            //即使存在消费者抢锁,也只有仓库里面有东西才能够购买
            if( num > 0 )
            {
                printf("消费者2进入仓库买产品,当前剩余%d个产品\n",num);
                num--;
                printf("消费者2购买完毕,当前剩余%d个产品\n",num);   
                printf("消费者2正在使用产品\n");         
            } 


            //解锁
            pthread_mutex_unlock(&mutex);

            //消费者使用产品需要3s
            
            sleep(3);
        }
    }
    return NULL; 
}



int main(int argc, char const *argv[])
{
    //初始化锁
    pthread_mutex_init(&mutex, NULL);
    //初始化条件变量
    pthread_cond_init(&cond,NULL);

    //创建三个线程,两个消费者一个生产者
    pthread_t cus , cus2 , creat;

    //生产者线程创建
    pthread_create(&creat, NULL, creat_fun , "生产者");
    //消费者线程创建
    pthread_create(&cus, NULL, custmer1_fun , "消费者1");
    pthread_create(&cus2, NULL, custmer2_fun , "消费者2");

    //回收线程
    pthread_join(creat, NULL);
    pthread_join(cus, NULL);
    pthread_join(cus2, NULL);


    //销毁锁
    pthread_mutex_destroy(&mutex);
    //销毁条件变量
    pthread_cond_destroy(&cond);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_72372635/article/details/131394690
今日推荐