Linux线程同步---条件变量(生产者消费者模型)

我们来理解一下条件变量,通俗的来讲就是条件变量的作用在于给多个线程提供了一个汇合的场所。举个例子说明一下,运动会赛跑中,所有选手都会等到发令枪响后才开跑,把选手比作其他线程,发令员比作主线程,意思就是所有的线程都等待主线程给予一个可以运行的信号,如果没有给信号,那么将会阻塞下去。

1.条件变量的概念

上一篇中我们介绍的是互斥量,而条件变量与互斥量不同,互斥量是防止多线程同时访问共享的互斥变量来保护临界区,只有两种状态:锁定和非锁定

而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足。简单的来说,就是设置一个条件变量让线程1等待在一个临界区的前面,当其他线程给这个变量执行通知操作时,线程1才会被唤醒,继续向下执行。条件变量总是和互斥量一起使用互斥量保护着条件变量,防止多个线程对条件变量产生竞争。

当条件变量满足时,线程通常解锁并等待条件变量发生变化,一旦另一个线程修改了条件变量,就会通知相应的条件变量唤醒一个或多个被该条件变量阻塞的线程。这些被唤醒的线程重新上锁,并测试条件是否满足。一般来讲,条件变量用于线程同步,当条件变量不满足时,允许其中一个执行流挂起和等待。

2.函数接口

条件变量的初始化&销毁函数
这里写图片描述
参数:
cond:条件变量
attr:条件变量属性
返回值:成功返回0,失败返回错误码

销毁函数所指定的条件变量,同时会释放所给它分配的资源。调用该函数的进程并不等待在参数所指定的条件变量上。

等待条件满足
这里写图片描述
pthread_cond_wait函数执行时先自动释放指定的锁,然后等待条件变量的变化,在函数返回之前,自动将指定的互斥量重新锁住。
pthread_cond_timewait函数与pthread_cond_wait的区别在于,如果达到或者超过所引用的参数abtime,将结束并发回错误ETIME。

唤醒等待
这里写图片描述
其中,pthread_cond_signal只唤醒一个在相同条件变量中阻塞的线程,pthread_cond_broadcast唤醒等待队列中所有线程
注意:调用pthread_cond_signal后要立刻释放互斥锁,因为pthread_cond_wait最后一步是要将指定的互斥量重新锁住,若pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞。

3.条件变量使用规范

等待条件

pthread_mutex_lock(&mutex);
while(条件为假)
   ptnread_cond_wait(cond, mutex);
修改条件
pthread_mutex_unlock(&mutex);

给条件发送信号

pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);

4.举个典型例子->生产者消费者模型
这里写图片描述
- 满足互斥和同步条件,用互斥锁和条件变量实现
- 多个生产者和消费者,其中生产者和生产者属于互斥关系,生产者和消费者属于同步互斥关系,消费者和消费者属于竞争关系,需要互斥锁

生产者和消费者模型可以归结为:三种关系,两种角色,一个交易场所
这里写图片描述
代码实现:

#include <stdio.h>                  
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>

typedef struct LinkNode{
    int data;
    struct LinkNode *next;
}Node, *pNode;

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;


pNode Create(int data){
    pNode NewNode = (pNode)malloc(sizeof(Node));
    if(NewNode == NULL){
        perror("malloc");
        return NULL;
    }
    NewNode->data = data;
    NewNode->next = NULL;
    return NewNode;
}

void InitLink(pNode* head){
    *head = Create(0);
}

int IsEmpty(pNode head){
    assert(head);
    if(head->next)
        return 0;
    else
        return 1;
}

void PushFront(pNode head, int data){
    assert(head);
    pNode node = Create(data);
    node->next = head->next;
    head->next = node;
}

void PopFront(pNode head, int *data){                                       
    assert(head);
    assert(data);
    if(IsEmpty(head)){
        printf("Link is empty\n");
        return;
    }
    pNode del = head->next;
    *data = del->data;
    head->next = del->next;
    free(del);
    del->data = 0;
    del->next = NULL;
}

void Display(pNode head){
    assert(head);
    pNode cur = head->next;
    while(cur){
        printf("%d ",cur->data);
        cur = cur->next;
    }
    printf("\n");
}                                                                           
void Destory(pNode head){
    int data = 0;
    assert(head);
    while(!IsEmpty(head)){
        PopFront(head, &data);
    }
    free(head);
}

void *product_run(void *arg){
    int data = 0;
    pNode head = (pNode)arg;
    while(1){
        usleep(1);
        data=rand()%1000;
        pthread_mutex_lock(&lock);
        PushFront(head, data);
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond);
        printf("product is done, data=%d\n",data);
    }
}                                                                 
void *consumer_run(void *arg){
    int data = 0;
    pNode head = (pNode)arg;
    while(1){
        pthread_mutex_lock(&lock);
        while(IsEmpty(head)){
             pthread_cond_wait(&cond, &lock);
         }
         PopFront(head, &data);
         pthread_mutex_unlock(&lock);
         printf("consumer is done,data=%d\n",data);
     }

}

void test()
{
     pNode head = NULL;
     InitLink(&head);
     pthread_t tid1;
     pthread_t tid2;

     pthread_create(&tid1, NULL, product_run, (void*)head);
     pthread_create(&tid2, NULL, consumer_run, (void*)head);

     pthread_join(tid1,NULL);
     pthread_join(tid2,NULL);
     Destory(head);
     pthread_mutex_destroy(&lock);
     pthread_cond_destroy(&cond);
}

int main()
{
     test();
}        

注意看到这里wait等待的时候是一个while不是if,这是为了防止“惊群效应”,例如两个线程同时阻塞在wait,先后醒来,快的线程先做完处理后把条件改变了,并且对互斥量解锁,此时慢的线程在wait里获得了锁返回,再去做处理就会出问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值