實驗名稱:經典同步問題:生成者與消費者問題
相關知識
信号量
信号量是用來協調不同程序間的資料對象,可用來保護共享資源,也能用來實作程序間及同一程序不同線程間的程序同步。分為二值信号燈和計算信号燈兩種類型。
程序與線程原語的比較
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLygzYxM2MjF2Y5YWY2UWNiVGNhRDOlVTNyIjM1YWMhR2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
線程建立
線程建立是通過函數
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
不是Linux預設庫,連結時需要使用靜态庫
pthread
。
libpthread.a