生產者消費者模型

對於生產者消費者模型,必須滿足以下三點:

①3種關係:生產者與生產者之間互斥,消費者與消費者之間互斥,生產者與消費者之間互斥且同步
②2種角色:生產者,消費者
③1種交易場所

本文根據交易場所,討論兩種生產者消費者模型:基於單鏈表和基於環形隊列

1.基於單鏈表的單生產者單消費者模型:
背景知識:互斥鎖和條件變量(對於互斥鎖這裏不加贅述,前面文章已經詳細討論過http://blog.csdn.net/chenkaixin_1024/article/details/73294394,這裏重點討論條件變量)

條件變量用於描述臨界資源的內部狀態,實現線程間同步機制,與互斥鎖搭配使用(在互斥鎖的保護下使用),實現線程的同步與互斥

條件變量的創建,初始化,銷燬與互斥鎖類似,條件變量類型:pthread_cond_t

初始化,銷燬接口如下:
初始化:int pthread_cond_init(pthread_cond_t* cond,const pthread_condattr_t* attr);
             當條件變量爲全局變量或者是靜態變量時,可以直接用PTHEAD_COND_INITIALIZER進行初始化(相當於初始化函數第二個參數爲NULL的情況)
銷燬:int pthread_cond_destory(pthread_cond_t* cond);
返回值:若成功返回0,否則返回錯誤碼

大多數情況下,我們將條件變量與互斥鎖組合起來使用,而我們的條件變量則是用來阻塞當前線程的,直到達到某個條件時,解除阻塞
所以,對於條件變量,最主要的操作就是等待和解除阻塞,接口如下:
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);

上述兩個函數用於等待,均爲阻塞式等待,它們均做以下幾步:
1. 釋放互斥鎖
2. 阻塞等待
3. 當被喚醒時,重新獲得互斥鎖並返回
對於後者的第三個參數,可以設定等待超時,如果到達了abstime所指定的 時刻仍然沒有別的線程來喚醒當前線程,就返回ETIMEDOUT
注意:pthread_cond_wait很有可能調用失敗,從而導致無法阻塞,由此出錯,所以對於pthread_cond_wait要放在循環當中

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
上述兩個函數用於喚醒線程,前者用於喚醒另外一個線程,後者用於喚醒在這個條件變量下所有阻塞的線程

下面是具體對於基於單鏈表的單生產者單消費者模型的實現:

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

typedef struct list{
    struct list* _next;
    int _val;
}Node,*pNode;

pNode pHead=NULL;
pthread_mutex_t Mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t needproduct=PTHREAD_COND_INITIALIZER;

void* product(void* arg)
{
    pNode pcur=NULL;
    while(1)
    {
        sleep(2);
        int value=rand()%100;
        pthread_mutex_lock(&Mutex);
        if(pHead==NULL)
        {
//            printf("3\n");
            pHead=(pNode)malloc(sizeof(Node));
            pHead->_next=NULL;
            pHead->_val=value;
            pcur=pHead;
        }
        else
        {
//            printf("1\n");
            pNode p=(pNode)malloc(sizeof(Node));
            pcur->_next=p;
            p->_val=value;
            p->_next=NULL;
            pcur=p;
//            printf("...");
        }
        printf("producter:%d\n",value);
//        printf("pHead->%d\n",pHead->_val);
        pthread_mutex_unlock(&Mutex);
        pthread_cond_signal(&needproduct);
    }
}

void* consum(void* arg)
{
    while(1)
    {
//        printf("2\n");
        sleep(1);
        pthread_mutex_lock(&Mutex);
//        printf("consumer\n");
        while(pHead==NULL)
        {
            pthread_cond_wait(&needproduct,&Mutex);//pthread_cond_wait調用失敗
        }
//        printf("4\n");
        printf("consumer:%d\n",pHead->_val);
        pNode pcur=pHead;
        pHead=pcur->_next;
        free(pcur);

        pthread_mutex_unlock(&Mutex);
    }
}


int main()
{
    pthread_t tid1,tid2;
    int err=pthread_create(&tid1,NULL,product,NULL);
    if(err!=0)
    {
        printf("pthread_create product:%s\n",strerror(err));
        return -1;
    }

    err=pthread_create(&tid2,NULL,consum,NULL);
    if(err!=0)
    {
        printf("pthread_create consum:%s\n",strerror(err));
        return -1;
    }


    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    return 0;
}

2.基於環形隊列的單生產者單消費者模型:
背景知識:信號量
嚴格來說,互斥鎖用0,1表示當前資源是否被佔用,從而限制線程對資源的申請,實現加鎖的目的,但是在某種程度上,我們也可以這麼來看,0,1來表示當前資源的可用數目,當可用數目爲0時,所有線程在對該資源進行申請時就會失敗,當爲1時,這個資源就可以被申請成功。
而對於我們的信號量而言,也就是用來描述臨界資源的可用數量,信號量變量類型爲sem_t,其具體操作如下:
①信號量的初始化與銷燬
int sem_init(sem_t *sem,int pshared,unsigned int value);
函數功能:用於信號量的初始化
參數:第一個參數表示指向信號量的指針,第二個參數表示當它爲0時表示實現線程間的同步,非0表示實現進程間的同步,第三個參數表示可用資源的數目
返回值:若成功返回0,否則返回-1且設置errno

int sem_destroy(sem_t* sem);
函數功能:用於釋放信號量

②信號量的P操作和V操作
P操作:int sem_wait(sem_t* sem);
函數功能:用於獲得資源,使信號量對應描述的資源數目減1,當調用sem_wait時資源數目已經爲0時,掛起等待
注意:若不希望掛起等待的話,可以調用sem_trywait

V操作:int sem_post(sem_t* sem);
函數功能:可以進行釋放資源,且對信號量所描述的資源數目加1,同時喚醒由於資源數目先前爲0而掛起等待的線程

對於基於環形隊列的生產者消費者模型,生產者與消費者之間得滿足以下幾點:

1.生產者不能將消費者套一個圈(即數據不能被覆蓋)
2.消費者不能超過生產者(即只有生產者生產了,消費者才能消費)
3.生產者與消費者不能訪問同一位置(即不能這裏生產者在放數據,那裏消費者在消費該位置的數據)

下面是對基於環形隊列的單生產者單消費者模型的實現:

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#define MAX_PRODUCT 10

sem_t data_sem;
sem_t blank_sem;
int queue[MAX_PRODUCT];

void init_queue()
{
    int i=0;
    for(;i<MAX_PRODUCT;i++)
    {
        queue[i]=0;
    }
}

void P(sem_t* sem)
{
    sem_wait(sem);
}

void V(sem_t* sem)
{
    sem_post(sem);
}

void* product(void* arg)
{
    int idx=0;
    while(1)
    {
        sleep(2);
        P(&blank_sem);
        int value=rand()%100;
        queue[idx%MAX_PRODUCT]=value;
        idx++;
        printf("product:%d\n",value);
        V(&data_sem);
    }
}

void* consum(void* arg)
{
    int idx=0;
    while(1)
    {
        sleep(1);
        P(&data_sem);
        int value=queue[idx%MAX_PRODUCT];
        idx++;
        printf("consum:%d\n",value);
        V(&blank_sem);
    }
}


int main()
{
    sem_init(&data_sem,0,0);
    sem_init(&blank_sem,0,MAX_PRODUCT);
    init_queue();
    pthread_t tid1,tid2;
    int err=pthread_create(&tid1,NULL,product,NULL);
    if(err!=0)
    {
        printf("pthread_create:%s\n",strerror(err));
        return -1;
    }

    err=pthread_create(&tid2,NULL,consum,NULL);
    if(err!=0)
    {
        printf("pthread_create:%s\n",strerror(err));
        return -1;
    }


    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    sem_destroy(&blank_sem);
    sem_destroy(&data_sem);
    return 0;
}

而對於多生產者多消費者模型而言,則要考慮生產者與生產者之間的互斥,消費者與消費者之間的互斥,所以我們加入互斥鎖來實現:

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#define MAX_PRODUCT 10

sem_t data_sem;
sem_t blank_sem;
int queue[MAX_PRODUCT];

void init_queue()
{
    int i=0;
    for(;i<MAX_PRODUCT;i++)
    {
        queue[i]=0;
    }
}

void P(sem_t* sem)
{
    sem_wait(sem);
}

void V(sem_t* sem)
{
    sem_post(sem);
}

pthread_mutex_t P_Mutex=PTHREAD_MUTEX_INITIALIZER;
int p=0;

void* product1(void* arg)
{
    int idx=0;
    while(1)
    {
        sleep(2);
        pthread_mutex_lock(&P_Mutex);
        P(&blank_sem);
        int value=rand()%100;
        queue[p%MAX_PRODUCT]=value;
        p++;
        printf("product1:%d\n",value);
        V(&data_sem);
        pthread_mutex_unlock(&P_Mutex);
    }
}


void* product2(void* arg)
{
    int idx=0;
    while(1)
    {
        sleep(2);
        pthread_mutex_lock(&P_Mutex);
        P(&blank_sem);
        int value=rand()%100;
        queue[p%MAX_PRODUCT]=value;
        p++;
        printf("product2:%d\n",value);
        V(&data_sem);
        pthread_mutex_unlock(&P_Mutex);
    }
}

pthread_mutex_t C_Mutex=PTHREAD_MUTEX_INITIALIZER;
int c=0;

void* consum1(void* arg)
{
    while(1)
    {
        sleep(1);
        pthread_mutex_lock(&C_Mutex);
        P(&data_sem);
        int value=queue[c%MAX_PRODUCT];
        c++;
        printf("consum1:%d\n",value);
        V(&blank_sem);
        pthread_mutex_unlock(&C_Mutex);
    }
}

void* consum2(void* arg)
{
    while(1)
    {
        sleep(1);
        pthread_mutex_lock(&C_Mutex);
        P(&data_sem);
        int value=queue[c%MAX_PRODUCT];
        c++;
        printf("consum2:%d\n",value);
        V(&blank_sem);
        pthread_mutex_unlock(&C_Mutex);
    }
}

int main()
{
    sem_init(&data_sem,0,0);
    sem_init(&blank_sem,0,MAX_PRODUCT);
    init_queue();
    pthread_t tid1,tid2,tid3,tid4;
    int err=pthread_create(&tid1,NULL,product1,NULL);
    if(err!=0)
    {
        printf("pthread_create:%s\n",strerror(err));
        return -1;
    }

    err=pthread_create(&tid2,NULL,product2,NULL);
    if(err!=0)
    {
        printf("pthread_create:%s\n",strerror(err));
        return -1;
    }

    err=pthread_create(&tid3,NULL,consum1,NULL);
    if(err!=0)
    {
        printf("pthread_create:%s\n",strerror(err));
        return -1;
    }


    err=pthread_create(&tid4,NULL,consum2,NULL);
    if(err!=0)
    {
        printf("pthread_create:%s\n",strerror(err));
        return -1;
    }


    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);

    sem_destroy(&blank_sem);
    sem_destroy(&data_sem);
    return 0;
}

















發佈了62 篇原創文章 · 獲贊 9 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章