生产者消费者模型

生产者消费者模型

互斥同步篇

三种关系两个角色一个场所。

生产者和消费者对临界区的访问是互斥的,各自也是互斥的。

例如链表生产节点的例子,生产者往链表里push,消费者pop,这时他们共同操纵的链表就是临界资源,不能让生产者消费者同时进入临界区,需要加以互斥条件。又假如生产者慢,消费者快,必须对消费者使用同步限制,不然产生饥饿现象。

第一个版本,不加互斥限制

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <pthread.h>
typedef struct Node{
    int data;
    struct Node* pNext;
}node,*pNode;

pNode pHead = NULL;

void init_llist(pNode *pHead)
{
    *pHead = (pNode)malloc(sizeof(node));
    if(NULL == *pHead){
        exit(1);
    }
    (*pHead)->pNext = NULL;
}


void show_llist(pNode pHead)
{
    if(pHead == NULL){
        return ;
    }
    pNode pCur = pHead->pNext;
    while(pCur){
        printf("%d->", pCur->data);
        pCur = pCur->pNext;
    }
    printf("\n");
}

void push_llist(pNode pHead, int data)
{
    pNode pNew = NULL;
    pNew = (pNode)malloc(sizeof(node));
    pNew->data = data;
    if(pNew != NULL){
        pNew->pNext = pHead->pNext;
        pHead->pNext = pNew;
        printf("生产者放入了一个%d\n", pNew->data);
    }       
}

void pop_llist(pNode pHead)
{
    if(pHead->pNext == NULL){
        printf("消费者来消费了,但是没有东西可拿\n");
        return ;
    }
    pNode pDel = pHead->pNext;
    printf("消费者拿走%d\n", pDel->data);
    pHead->pNext = pHead->pNext->pNext;
    free(pDel);
}

void* productor()
{
    srand((unsigned)time(NULL));
    //每过0.5秒生产一个
    while(1)
    {
        int data = rand()%100 +1;
        push_llist(pHead, data);    
        show_llist(pHead);
        fflush(stdout);
        usleep(500000);
    }
    return NULL;
}

void* consumer()
{
    while(1){
        //每过0.5秒消费一个
        pop_llist(pHead);
        usleep(500000);
    }
    return NULL;
}
int main()
{
    //带头结点的单链表
    init_llist(&pHead);
    //创建2个线程
    pthread_t tidA, tidB;
    void *ret;
    pthread_create(&tidA, NULL, productor, NULL);
    pthread_create(&tidB, NULL, consumer, NULL);
    pthread_join(tidA, &ret);
    free(ret);
    pthread_join(tidB, &ret);
    free(ret);
}

打印结果:

消费者来消费了,但是没有东西可拿
生产者放入了一个18
18->
消费者拿走18
生产者放入了一个17
0->

第一个18正常,然后生产者放了17,链表却被修改成了0,发生错乱,这就是两个进程同时访问临界区的后果。

加入互斥条件

对临界区进行限制就不会出错了

pthread_mutex_t mutex;
void* productor()
{
    srand((unsigned)time(NULL));
    //每过3秒生产一个
    while(1)
    {
        pthread_mutex_lock(&mutex);
        int data = rand()%100 +1;
        push_llist(pHead, data);    
        show_llist(pHead);
        fflush(stdout);
        pthread_mutex_unlock(&mutex);
        usleep(2000000);
    }
    return NULL;
}

void* consumer()
{
    while(1){
        pthread_mutex_lock(&mutex);
        pop_llist(pHead);
        pthread_mutex_unlock(&mutex);
        usleep(500000);
    }
    return NULL;
}

生产者放入了一个45
45->
消费者拿走45
消费者来消费了,但是没有东西可拿
消费者来消费了,但是没有东西可拿
消费者来消费了,但是没有东西可拿

加入了互斥条件,这样怎么都不会再出错了。

但现在有新的问题,消费者频繁的抢占对临界区的访问却拿不到资源,这就极大的浪费了系统资源。所以需要对临界资源实现有序访问,加入线程同步。

加入线程同步

pthread_mutex_t mutex;
pthread_cond_t cond;
void* productor()
{
    srand((unsigned)time(NULL));
    //每过3秒生产一个
    while(1)
    {
        pthread_mutex_lock(&mutex);
        int data = rand()%100 +1;
        push_llist(pHead, data);    
        show_llist(pHead);
        fflush(stdout);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
        usleep(2000000);
    }
    return NULL;
}

void* consumer()
{
    while(1){
        pthread_mutex_lock(&mutex);
        while(pHead->pNext == NULL)
            pthread_cond_wait(&cond, &mutex);
        pop_llist(pHead);
        pthread_mutex_unlock(&mutex);
        usleep(500000);
    }
    return NULL;
}

生产者放入了一个87
87->
消费者拿走87
生产者放入了一个26
26->

消费者在进到临界区后,对临界资源进行判断,如果条件不满足,就挂起等待需求的资源。

信号量版

信号量是用来做线程同步的,因为多线程看待信号量也是临界资源,因此想要起到同步进程的作用首先得保证信号量自己是原子性的。

信号量的本质就是计数器,通过描述临界资源的数量来同步线程,只要线程申请到信号量,在临界区内一定有一个资源属于线程。

一个放苹果的例子

一个环形队列,假设有6个空盘子,一个人不断往盘子里放苹果,一个人跟在屁股后面拿苹果,还要保证2个规则

  1. 放苹果的人只能往空盘子里放
  2. 拿苹果的人只能从有苹果的盘子拿

再设想两种情况

  1. 一开始没有苹果的时候,只允许放苹果的人操作
  2. 放满的时候只允许拿苹果的人操作

这里苹果和6个盘子就是临界资源,放盘子的桌子是临界区,两个人即使同时进入临界区也没关系,因为操纵各自的临界资源并不会冲突,即使两个人操作到同一个盘子也规定好了谁拿谁放,所以并不需要线程互斥,只需要同步就好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值