信号量
前面我們在基于阻塞隊列的生産者消費者模型 今天我們來看一下基于信号量的生産者消費者模型,也可以說是基于環形隊列的。
POSIX信号量
POSIX信号量和SystemV信号量作用相同,都是用于同步操作,達到無沖突的通路共享資源目的。 但POSIX可以用于線程間同步
初始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
參數:
pshared:0表示線程間共享,非零表示程序間共享
value:信号量初始值
銷毀信号量
int sem_destroy(sem_t *sem);
等待信号量
功能:等待信号量,會将信号量的值減1
int sem_wait(sem_t *sem);
釋出信号量
功能:釋出信号量,表示資源使用完畢,可以歸還資源了。将信号量值加1。
int sem_post(sem_t *sem);
基于環形隊列的生産者消費者模型
- 環形隊列采用數組模拟,用模運算來模拟環狀特性
- 環形結構起始狀态和結束狀态都是一樣的,不好判斷為空或者為滿,是以可以通過加計數器或者标記位來判斷滿或者空。另外也可以預留一個空的位置,作為滿的狀态
- 但是我們現在有信号量這個計數器,就很簡單的進行多線程間的同步過程
- 簡單的總結一句話就是,你不能超過我,我不能把你套個圈,這裡你是消費者,我是生産者
源碼展示
- ring.hpp
#ifndef __RING_HPP__
#define __RING_HPP__
#include <iostream>
#include <semaphore.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#define NUM 32
using namespace std;
class Ring{
private:
int circle[NUM];//環形隊列
int cap;//信号量的容量
int c_step;//消費者下标
int p_step;//生産者下标
sem_t sem_data;//消費者可以消費的資料
sem_t sem_blank;//生産者可以生産的位置
void P(sem_t &s)
{
sem_wait(&s);
}
void V(sem_t &s)
{
sem_post(&s);
}
public:
Ring():cap(NUM)
{
c_step = p_step = 0;
sem_init(&sem_data,0,0);
sem_init(&sem_blank,0,NUM);
}
//消費者調用該接口進行消費
void GetData(int& out)
{
P(sem_data);//消費者申請資料資源消費
out = circle[c_step];//消費者從環形隊列中取資料
c_step++;
V(sem_blank);//消費者增加空間
c_step %= cap;//當走到最後時重新置為0
}
//生産者調用該接口進行生産
void PutData(const int& in)
{
P(sem_blank);//生産者申請空間生産
circle[p_step] = in;//往環形隊列中插入資料
p_step++;//下标++
V(sem_data);//生産者增加資料
p_step %= cap;//因為是環形隊列,當走到下标最後時将它重置為0
}
~Ring()
{
sem_destroy(&sem_data);
sem_destroy(&sem_blank);
}
};
#endif
- ring.cc
#include "ring.hpp"
#include <time.h>
void* consumer(void* arg)
{
Ring* r = (Ring*)arg;
int data;
for(;;){
r->GetData(data);
cout << "consumer data:"<< data <<endl;
}
}
void* producter(void* arg)
{
Ring* r = (Ring*)arg;
for(;;){
int data = rand() % 100 + 1;
r->PutData(data);
cout << "product data:"<< data <<endl;
sleep(1);
}
}
int main()
{
srand((unsigned long)time(NULL));//生成随機數
Ring r;//定義一個環形隊列,基于信号量
pthread_t c,p;//建立兩個線程c為消費者,p為生産者
pthread_create(&c,NULL,consumer,(void*)&r);//讓生産者和消費者都看到該環形隊列
pthread_create(&p,NULL,producter,(void*)&r);
pthread_join(c,NULL);
pthread_join(p,NULL);
return 0;
}
- Makefile
ring:ring.cc
g++ -o [email protected] $^ -lpthread
.PHONY:clean
clean:
rm -f ring