天天看點

實驗名稱:經典同步問題:生成者與消費者問題

實驗名稱:經典同步問題:生成者與消費者問題

相關知識

信号量

信号量是用來協調不同程序間的資料對象,可用來保護共享資源,也能用來實作程序間及同一程序不同線程間的程序同步。分為二值信号燈和計算信号燈兩種類型。

程序與線程原語的比較

實驗名稱:經典同步問題:生成者與消費者問題

線程建立

線程建立是通過函數

pthread_create(thread,attr,start_routine,arg)

函數來實作的,而該函數是通過Linux特有的系統調用

clone

來實作的。

格式:

#include<pthread.h>
int pthread_create(thread,attr,start_routine,arg);
           

其中參數

thread

為線程辨別符,

attr

為線程屬性設定,

start_routine

為線程函數起始位址,

arg

為傳遞給

start_routine

的參數。建立線程成功傳回0,否則傳回錯誤号。

獲得線程辨別符

格式:

#include<pthread.h>
pthread_t pthread_self(void);
           

說明:傳回調用的線程的辨別符。每個線程都有自己的線程辨別符,以便在程序内區分,線程辨別符在

pthread_create

建立時産生。

線程等待

格式:

#include<pthread.h>
int pthread_join(thread,retval);
           

說明:該函數将調用它的線程阻塞,一直等到被等待的線程結束為止,當函數傳回時,被等待線程的資源被收回。

thread

為被等待的線程辨別符,

retval

為使用者定義的指針,存放被等待線程的傳回值。

線程退出

格式:

#include<pthread.h>
void pthread_exit(retval); //終止調用線程,`retval`為線程的傳回值。

int pthread_cancel(thread); //終止由參數thread指定的線程
           

實驗内容

使用多線程和信号量解決生産者/消費者問題:有一個長度為N的緩沖池被生産者和消費者共同使用。隻要緩沖池未滿,生産者就可以将消息送入緩沖池;隻要緩沖池不空,消費者便可從緩沖池中取走一個消息。生産者向緩沖池放入消息的同時,消費者不能操作緩沖池,反之亦然。

pthread_join()

将調用它的線程阻塞,一直等到被等待的線程結束為止,當函數傳回時,被等待線程的資源被收回。本實驗中使用

room_sem

信号量來表示緩沖區可用空間,

product_sem

信号量表示緩沖區中有無可用産品,而

mutex

代表線程互斥信号量。

編寫

producer_consumer.c

:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<sys/types.h>
#define PRODUCER_NUM 5
#define CONSUMER_NUM 5
#define POOL_SIZE 11
int pool[POOL_SIZE]; //buffer
int head=0; //read pointer
int rear=0; //write pointer
sem_t room_sem;//available room in buffer
sem_t product_sem;//available products in buffer
pthread_mutex_t mutex;
void producer_fun(void*arg)
{
        while(1)
        {
                sleep(1);
                sem_wait(&room_sem);
                pthread_mutex_lock(&mutex);
                //producer write data to buffer
                pool[rear]=1;
                rear=(rear+1)%POOL_SIZE;
                printf("producer %d write to pool\n",(int)arg);
                printf("pool size is %d\n",(rear-head+POOL_SIZE)%POOL_SIZE);
                pthread_mutex_unlock(&mutex);
                sem_post(&product_sem);
        }
}
void consumer_fun(void *arg)
{
        while(1)
        {
                int data;
                sleep(10);
                sem_wait(&product_sem);
                pthread_mutex_lock(&mutex);
                //consumer read data in buffer
                data=pool[head];
                head=(head+1)%POOL_SIZE;
                printf("consumer %d read from pool\n",(int)arg);
                printf("pool size is %d\n",(rear-head+POOL_SIZE)%POOL_SIZE);
                pthread_mutex_unlock(&mutex);
                sem_post(&room_sem);
        }
}
int main()
{
        pthread_t producer_id[PRODUCER_NUM];
        pthread_t consumer_id[CONSUMER_NUM];
        pthread_mutex_init(&mutex,NULL);
        int ret=sem_init(&room_sem,0,POOL_SIZE-1);//initialize the signal room_sem
        if(ret!=0)
        {
                printf("sem_init error\n");
                exit(0);
        }
        ret=sem_init(&product_sem,0,0); //initialize the signal produc_sem
        if(ret!=0)
        {
                printf("sem_init error\n");
                exit(0);
        }
        for(int i=0;i<PRODUCER_NUM;i++)
        {
                //create producer thread
                ret=pthread_create(&producer_id[i],NULL,producer_fun,(void*)i);
                if(ret!=0)
                {
                        printf("producer_id error\n");
                        exit(0);
                }
                //create consumer thread
                ret=pthread_create(&consumer_id[i],NULL,consumer_fun,(void*)i);
                if(ret!=0)
                {
                        printf("consumer_id error\n");
                        exit(0);
                }
        }
        for(int i=0;i<PRODUCER_NUM;i++)
        {
                pthread_join(producer_id[i],NULL);
                pthread_join(consumer_id[i],NULL);
        }
        exit(0);
}
           

編譯時使用以下指令:

gcc -o producer_consumer producer_consumer.c -lpthread

注:編譯選項要加上

-lpthread

,因為

pthread

不是Linux預設庫,連結時需要使用靜态庫

libpthread.a

繼續閱讀