天天看點

【Linux】—— 基于信号量的生産者消費者模型

信号量

前面我們在基于阻塞隊列的生産者消費者模型 今天我們來看一下基于信号量的生産者消費者模型,也可以說是基于環形隊列的。

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
           

繼續閱讀