码迷,mamicode.com
首页 > 其他好文 > 详细

浅谈生产者消费者模型与读写者模型的区别

时间:2016-07-19 11:27:55      阅读:367      评论:0      收藏:0      [点我收藏+]

标签:线程

多线程编程在操作系统中是十分重要的。

而在线程中处理同步与互斥问题又是至关重要的。生产者-消费者模型,(也称有限缓冲问题)是一个多线程同步问题的经典例子。下来我们对其进行简单分析。

生产者——>生成一定量的数据放到缓冲区中,然后重复此过程;

消费者——>在缓冲区消耗这些数据。

而生产者-消费者之间存在三种关系,即

生产者与生产者之间是互斥关系;

消费者与消费者之间是互斥关系;

生产者与消费者之间是同步与互斥关系。

下面的程序演示了一个生产者-消费者的例子,生产者生产一个结构体串在链表的表头上,消费者从表头取走结构体。(基于互斥锁)

#include<stdio.h>
#include<malloc.h>
#include<pthread.h>
typedef int  _dataType_;
typedef int*  _dataType_p_;
typedef struct _node
{
    _dataType_ data;
    struct _node* next;
}node,*nodep,**nodepp;
nodep head=NULL;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
nodep  buyNode(_dataType_ val)
{
    nodep tmp=(nodep)malloc(sizeof(node));
    if(tmp!=NULL)
    {
        tmp->data=val;
        tmp->next=NULL;
        return tmp;
    }
    return NULL;
}
void init(nodepp head)
{
    *head=buyNode(0);
}
void push_list(nodep head,_dataType_ val)
{
    nodep tmp=buyNode(val);
    tmp->next=head->next;
    head->next=tmp;
}
int pop_list(nodep head,_dataType_p_ pval)
{
    if(head->next==NULL)
        return -1;
    nodep del=head->next;
    *pval=del->data;
    head->next=del->next;
    free(del);
    return 0;
}
void* product(void* arg)
{
    _dataType_  i=0;
    while(1)
    {
        sleep(1);
        pthread_mutex_lock(&mutex);
        push_list(head,i++);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit((void*)1);
}
void* consumer(void* arg)
{
    _dataType_ val=0;
    while(1)
    {
        sleep(1);
        pthread_mutex_lock(&mutex);
        if(pop_list(head,&val)==-1)
            pthread_cond_wait(&cond,&mutex);
        printf("data:%d\n",val);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit((void*)1);
}
int main()
{
    pthread_t tid1,tid2;
    init(&head);
    pthread_create(&tid1,NULL,product,NULL);
    pthread_create(&tid2,NULL,consumer,NULL);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    free(head);
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
    return 0;
}


上述的生产者-消费者的例子是基于链表的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序(使用POSIX信号量)。

它有以下特征:

生产者不能把消费者套圈、消费者不能超过生产者;

数据为空不消费;

数据为满不生产;

环形队列的基本模型如下:

技术分享

实现如下:

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#define _SEM_PRO_ 20
#define _SEM_COM_ 0
typedef int  _dataType_;
_dataType_ blank[_SEM_PRO_];
sem_t sem_product;
sem_t sem_consumer;
void* product(void* arg)
{
    int index=0;
    int count=0;
    while(1)
    {
        sleep(rand()%5);
        sem_wait(&sem_product);
        blank[index++]=count++;
        sem_post(&sem_consumer);
        index%=_SEM_PRO_;
    }
    pthread_exit((void*)1);
}
void* consumer(void* arg)
{
    int index=0;
    while(1)
    {
        sem_wait(&sem_consumer);
        printf("data:%d\n",blank[index++]);
        sem_post(&sem_product);
        index%=_SEM_PRO_;
    }
    pthread_exit((void*)1);
}
int main()
{
    pthread_t tid1,tid2;
    sem_init(&sem_product,0,20);
    sem_init(&sem_consumer,0,0);
    pthread_create(&tid1,NULL,product,NULL);
    pthread_create(&tid2,NULL,consumer,NULL);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    sem_destroy(&sem_product);
    sem_destroy(&sem_consumer);
    return 0;
}

       生产者-消费者问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

       要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。          通常可以采用进程间通信的方法解决该问题。如果解决办法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。


      接下来讨论另一种经典例子,读者-写者模型,也就是读写锁。

      读者-写者模式也存在三种关系两种方式一个场所,即:

      三种关系:读者与读者:无关系;

                       写者与写者:互斥关系;

                       读者与写者:同步与互斥关系;

      两种方式:读者优先(以读者优先) 、写者优先(以写者为核心)。

       一个场所:同一临界资源(数据)。

      自旋锁:如果所等待条件不满足,不挂起,一直申请锁。适宜某个线程占用锁时间较短。

当不断有多个读者准备读时,如果有写者到来,此时应该当前读者读完后,提高写者优先级,下个进入临界区的是写者。(写者优先,否则会造成写者饥饿

当不断有多个写者准备写时,如果有读者到来,此时应该当前写者写完后,提高读者优先级,下个进入临界区的是读者。(读者优先,否则会造成读者饥饿

代码如下:

#include<stdio.h>
#include<pthread.h>
int g_val=0;
pthread_rwlock_t rw_lock;
void* reader(void* arg)
{
    while(1)
    {
        pthread_rwlock_rdlock(&rw_lock);
        printf("g_val: %d\n",g_val);
        pthread_rwlock_unlock(&rw_lock);
    }
}
void* writer(void* arg)
{
    while(1)
    {
        sleep(1);//使写者隔一秒竞争一次锁资源
        pthread_rwlock_wrlock(&rw_lock);
        g_val++;
        pthread_rwlock_unlock(&rw_lock);
    }
}
 
int main()
{
    pthread_t tid1,tid2;
    pthread_rwlock_init(&rw_lock,NULL);
    pthread_create(&tid1,NULL,reader,NULL);
    pthread_create(&tid2,NULL,writer,NULL);
    pthread_rwlock_destroy(&rw_lock);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    return 0;
}

       通过分析可以看出生产者-消费者模型与读者-写者模型之间的区别:

       1.前者完全是生产与消费的关系,后者中的读者在充当消费者的基础上实际上也在变相生产,充当了不自觉的生产者,而且这种再生产将不会停止。

       2.读者-写者问题是缓冲区只能有一个写者占用;而生产者-消费者问题是缓冲区存放的是多个产品,比如有物品消费者才能访问缓冲区。








本文出自 “七月朔风” 博客,请务必保留此出处http://luminous.blog.51cto.com/10797288/1827531

浅谈生产者消费者模型与读写者模型的区别

标签:线程

原文地址:http://luminous.blog.51cto.com/10797288/1827531

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!