最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux基于阻塞隊(duì)列的生產(chǎn)消費(fèi)者模型詳解

 更新時間:2025年04月27日 09:22:40   作者:s_little_monster_  
這篇文章主要介紹了Linux基于阻塞隊(duì)列的生產(chǎn)消費(fèi)者模型,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

一、什么是生產(chǎn)消費(fèi)者模型

生產(chǎn)消費(fèi)者模型就是通過一個容器來解決生產(chǎn)者和消費(fèi)者的強(qiáng)耦合問題,生產(chǎn)者和消費(fèi)者彼此之間不直接通訊,而是通過阻塞隊(duì)列來進(jìn)行通訊,所以生產(chǎn)者生產(chǎn)完數(shù)據(jù)之后不用等待消費(fèi)者處理,直接交給阻塞隊(duì)列,消費(fèi)者不找生產(chǎn)者索要數(shù)據(jù),而是直接從阻塞隊(duì)列中去取,這樣一來,阻塞隊(duì)列就相當(dāng)于一個緩沖區(qū),平衡了生產(chǎn)者和消費(fèi)者的處理能力

對于生產(chǎn)消費(fèi)者模型,我們有一個321規(guī)則,分別是3種關(guān)系,2種角色,1個交易場所

  • 三種關(guān)系:生產(chǎn)者和生產(chǎn)者的互斥競爭關(guān)系,消費(fèi)者和消費(fèi)者的互斥競爭關(guān)系,生產(chǎn)者和消費(fèi)者的互斥、同步關(guān)系
  • 兩種角色:生產(chǎn)者和消費(fèi)者
  • 一個交易場所:特定結(jié)構(gòu)的內(nèi)存空間(如阻塞隊(duì)列)

二、基于阻塞隊(duì)列的生產(chǎn)消費(fèi)者模型

1、理論研究

在多線程編程中,阻塞隊(duì)列是一種常用于實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模型的數(shù)據(jù)結(jié)構(gòu),其與普通的隊(duì)列區(qū)別在于,當(dāng)隊(duì)列為空時,從隊(duì)列獲取元素的操作將會被阻塞,直到隊(duì)列中再次被放入元素,當(dāng)隊(duì)列滿時,往隊(duì)列中存放元素的操作也會被阻塞,直到有元素從隊(duì)列中被獲取

生產(chǎn)消費(fèi)者模型最大的好處是,也是生產(chǎn)消費(fèi)者模型效率高的原因是:在消費(fèi)者獲取數(shù)據(jù)(一般是網(wǎng)絡(luò)數(shù)據(jù))的時候,生產(chǎn)者可以生產(chǎn)數(shù)據(jù),生產(chǎn)者放入數(shù)據(jù)的時候,消費(fèi)者可以處理數(shù)據(jù),雖然特定內(nèi)存結(jié)構(gòu),也就是臨界資源區(qū)是有鎖的,只能由單線程通過,只要將時間合理化,我們就可以實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者的高效率工作,并且將發(fā)送數(shù)據(jù)的線程和處理數(shù)據(jù)的線程解耦合

2、多生產(chǎn)多消費(fèi)模型

(一)BlockQueue.hpp

#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
//定義一個模版類,方便我們使用任何類型進(jìn)行生產(chǎn)消費(fèi)
template <class T>
//定義一個阻塞隊(duì)列
class BlockQueue
{
	//隊(duì)列默認(rèn)最大容量
    static const int defalutnum = 20;

public:
    BlockQueue(int maxcap = defalutnum) : maxcap_(maxcap)
    {
    	//初始化互斥鎖和生產(chǎn)者和消費(fèi)者的條件變量
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&c_cond_, nullptr);
        pthread_cond_init(&p_cond_, nullptr);
        //下面注釋掉的是設(shè)置水位線,設(shè)置最低最高水位線
        //在阻塞隊(duì)列中的數(shù)據(jù),在低于最低水位線時是不可被獲取的,只能寫入
        //在高于最高水位線時是不可被寫入的,只能獲取
        // low_water_ = maxcap_/3;
        // high_water_ = (maxcap_*2)/3;
    }

	//從隊(duì)列頭取出元素返回
    T pop()
    {
        pthread_mutex_lock(&mutex_);//加鎖
        //只能用while不能用if,原因是會出現(xiàn)誤喚醒問題,下面說
        while (q_.size() == 0)
        {
            pthread_cond_wait(&c_cond_, &mutex_); 
        }

        T out = q_.front();
        q_.pop();
		//這里是加了水位線的版本,在低于水位線的時候要喚醒生產(chǎn)者
        // if(q_.size()<low_water_) pthread_cond_signal(&p_cond_);
        pthread_cond_signal(&p_cond_); 
        pthread_mutex_unlock(&mutex_);//解鎖

        return out;
    }

    void push(const T &in)
    {
        pthread_mutex_lock(&mutex_);//加鎖
        //同pop函數(shù)
        while (q_.size() == maxcap_)
        {
            pthread_cond_wait(&p_cond_, &mutex_); 
        }
        q_.push(in); 
        
        //這里是加了水位線的版本,在高于水位線的時候要喚醒消費(fèi)者
        // if(q_.size() > high_water_) pthread_cond_signal(&c_cond_);
        pthread_cond_signal(&c_cond_);
        pthread_mutex_unlock(&mutex_);//解鎖
    }
	//析構(gòu)函數(shù)
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&c_cond_);
        pthread_cond_destroy(&p_cond_);
    }

private:
    std::queue<T> q_; 
    
    int maxcap_; // 極大值
    
    pthread_mutex_t mutex_;
    pthread_cond_t c_cond_;
    pthread_cond_t p_cond_;
    
	//最低最高水位線
    // int low_water_;
    // int high_water_;
};

(二)Task.hpp

#pragma once
#include <iostream>
#include <string>

//定義運(yùn)算方法
std::string opers = "+-*/%";

//枚舉錯誤
enum
{
    DivZero = 1,
    ModZero,
    Unknown
};

class Task
{
public:
    Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0)
    {}
    void run()
    {
        switch (oper_)
        {
        case '+':
            result_ = data1_ + data2_;
            break;
        case '-':
            result_ = data1_ - data2_;
            break;
        case '*':
            result_ = data1_ * data2_;
            break;
        case '/':
        {
            if (data2_ == 0)
                exitcode_ = DivZero;
            else
                result_ = data1_ / data2_;
        }
        break;
        case '%':
        {
            if (data2_ == 0)
                exitcode_ = ModZero;
            else
                result_ = data1_ % data2_;
        }
        break;
        default:
            exitcode_ = Unknown;
            break;
        }
    }
    //偽函數(shù),通過重載()使run可以像函數(shù)一樣調(diào)用
    void operator()()
    {
        run();
    }
    //返回的運(yùn)算結(jié)果以及錯誤代碼
    std::string GetResult()
    {
        std::string r = std::to_string(data1_);
        r += oper_;
        r += std::to_string(data2_);
        r += "=";
        r += std::to_string(result_);
        r += "[code: ";
        r += std::to_string(exitcode_);
        r += "]";

        return r;
    }
    //返回運(yùn)算表達(dá)式
    std::string GetTask()
    {
        std::string r = std::to_string(data1_);
        r += oper_;
        r += std::to_string(data2_);
        r += "=?";
        return r;
    }
    ~Task()
    {}

private:
    int data1_;
    int data2_;
    char oper_;

    int result_;
    int exitcode_;
};

(三)main.cpp

#include "BlockQueue.hpp"
#include "Task.hpp"
#include <unistd.h>
#include <ctime>

void *Consumer(void *args)
{
    BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);

    while (true)
    {
        // 消費(fèi)
        Task t = bq->pop();
        // 計算
        t();
        
		//模擬消費(fèi)者處理任務(wù)
        std::cout << "處理任務(wù): " << t.GetTask() << " 運(yùn)算結(jié)果是: " << t.GetResult() << " thread id: " << pthread_self() << std::endl;
    }
}

void *Productor(void *args)
{
    int len = opers.size();
    BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
    int x = 10;
    int y = 20;
    while (true)
    {
        // 用隨機(jī)數(shù)運(yùn)算模擬生產(chǎn)者生產(chǎn)數(shù)據(jù)
        int data1 = rand() % 10 + 1; // [1,10]
        usleep(10);
        int data2 = rand() % 10;
        char op = opers[rand() % len];
        Task t(data1, data2, op);

        // 生產(chǎn)
        bq->push(t);
        std::cout << "生產(chǎn)了一個任務(wù): " << t.GetTask() << " thread id: " << pthread_self() << std::endl;
        sleep(1);
    }
}

int main()
{
	//隨機(jī)數(shù)種子
    srand(time(nullptr));

    //給阻塞隊(duì)列傳一個任務(wù)
    BlockQueue<Task> *bq = new BlockQueue<Task>();
    //多生產(chǎn)者多消費(fèi)者
    pthread_t c[3], p[5];
    for (int i = 0; i < 3; i++)
    {
        pthread_create(c + i, nullptr, Consumer, bq);
    }

    for (int i = 0; i < 5; i++)
    {
        pthread_create(p + i, nullptr, Productor, bq);
    }

    for (int i = 0; i < 3; i++)
    {
        pthread_join(c[i], nullptr);
    }
    for (int i = 0; i < 5; i++)
    {
        pthread_join(p[i], nullptr);
    }
    delete bq;
    return 0;
}

3、誤喚醒問題

誤喚醒問題就是在調(diào)用pop函數(shù)或者push函數(shù)的時候可能會引起的,下面我們再把代碼貼出來,然后把上面有過的注釋去掉

//...

	T pop()
    {
        pthread_mutex_lock(&mutex_);
        while (q_.size() == 0) //不能調(diào)用if而要用while
        {
            pthread_cond_wait(&c_cond_, &mutex_); 
        }

        T out = q_.front();
        q_.pop();

        pthread_cond_signal(&p_cond_); 
        pthread_mutex_unlock(&mutex_);

        return out;
    }

    void push(const T &in)
    {
        pthread_mutex_lock(&mutex_);
        while (q_.size() == maxcap_)
        {
            pthread_cond_wait(&p_cond_, &mutex_); 
        }
        q_.push(in); 
        
        pthread_cond_signal(&c_cond_);
        pthread_mutex_unlock(&mutex_);
        
//...

在多生產(chǎn)者 - 多消費(fèi)者并發(fā)編程場景中,誤喚醒現(xiàn)象較為常見,假定隊(duì)列當(dāng)前處于滿狀態(tài),當(dāng)一個消費(fèi)者線程成功消費(fèi)一個數(shù)據(jù)后,隊(duì)列中會空出一個位置,隨后,線程可能多次調(diào)用pthread_cond_signal(&p_cond_) 函數(shù),喚醒了一批正在 p_cond_ 條件變量下等待的生產(chǎn)者線程,由于被喚醒的生產(chǎn)者線程需要重新競爭互斥鎖,這些線程之間呈現(xiàn)出互斥關(guān)系,在先前執(zhí)行消費(fèi)操作的線程釋放鎖之后,僅有一個生產(chǎn)者線程能夠成功獲取鎖,其余雖被喚醒但未能搶到鎖的生產(chǎn)者線程只能在鎖處等待

當(dāng)成功獲取鎖的生產(chǎn)者線程完成數(shù)據(jù)生產(chǎn)操作后,隊(duì)列可能再次達(dá)到滿狀態(tài),此時,該線程會調(diào)用 pthread_cond_signal(&c_cond_) 函數(shù)喚醒一個消費(fèi)者線程,隨后釋放鎖,在此情形下,被喚醒的線程不僅包括剛剛被喚醒的消費(fèi)者線程,還涵蓋之前被喚醒卻未搶到鎖的生產(chǎn)者線程,它們會同時參與鎖的競爭,若使用 if 語句來判斷隊(duì)列是否已滿,當(dāng)某個生產(chǎn)者線程搶到鎖后,可能不會再次對隊(duì)列狀態(tài)進(jìn)行檢查,直接嘗試向已滿的隊(duì)列中添加數(shù)據(jù),從而引發(fā)錯誤

因此,為確保線程安全,應(yīng)使用 while 循環(huán)來包裹 pthread_cond_wait 函數(shù),當(dāng)一個線程被喚醒并成功獲取鎖后,不應(yīng)直接執(zhí)行隊(duì)列操作(無論是生產(chǎn)數(shù)據(jù)還是消費(fèi)數(shù)據(jù)),而應(yīng)再次檢查資源是否滿足操作條件,若資源就緒,則可繼續(xù)執(zhí)行隊(duì)列操作;若資源未就緒,則應(yīng)再次調(diào)用 pthread_cond_wait 函數(shù),使線程進(jìn)入休眠狀態(tài),等待后續(xù)喚醒

總結(jié)

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Linux下nginx配置https協(xié)議訪問的方法

    Linux下nginx配置https協(xié)議訪問的方法

    這篇文章主要介紹了Linux下nginx配置https協(xié)議訪問的方法,需要的朋友可以參考下
    2016-07-07
  • 阿里云ECS云服務(wù)器如何開放8080端口

    阿里云ECS云服務(wù)器如何開放8080端口

    這篇文章主要介紹了阿里云ECS云服務(wù)器如何開放8080端口,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • PHP程序員玩轉(zhuǎn)Linux系列 lnmp環(huán)境搭建

    PHP程序員玩轉(zhuǎn)Linux系列 lnmp環(huán)境搭建

    這篇文章主要為大家詳細(xì)介紹了PHP程序員玩轉(zhuǎn)Linux系列文章,lnmp環(huán)境搭建教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Ubuntu如何修改Swap交換空間大小

    Ubuntu如何修改Swap交換空間大小

    這篇文章主要介紹了Ubuntu如何修改Swap交換空間大小問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • linux解決Tomcat內(nèi)存溢出的問題

    linux解決Tomcat內(nèi)存溢出的問題

    下面小編就為大家?guī)硪黄猯inux解決Tomcat內(nèi)存溢出的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • Linux搭建Docker環(huán)境全過程

    Linux搭建Docker環(huán)境全過程

    這篇文章主要介紹了Linux搭建Docker環(huán)境全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Linux系統(tǒng)下安裝rz/sz命令及使用說明(詳解)

    Linux系統(tǒng)下安裝rz/sz命令及使用說明(詳解)

    下面小編就為大家?guī)硪黄狶inux系統(tǒng)下安裝rz/sz命令及使用說明(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,祝大家游戲愉快哦
    2016-12-12
  • 阿里云OSS訪問權(quán)限配置(RAM權(quán)限控制)實(shí)現(xiàn)

    阿里云OSS訪問權(quán)限配置(RAM權(quán)限控制)實(shí)現(xiàn)

    這篇文章主要介紹了阿里云OSS訪問權(quán)限配置(RAM權(quán)限控制)實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • centos下fail2ban安裝與配置詳解

    centos下fail2ban安裝與配置詳解

    這篇文章主要介紹了centos下fail2ban安裝與配置實(shí)例,fail2ban是一個實(shí)用、強(qiáng)大的Linux安全軟件,可以監(jiān)控大多數(shù)常用服務(wù)器軟件,需要的朋友可以參考下
    2014-04-04
  • Linux實(shí)現(xiàn)搭建ssh并允許使用root遠(yuǎn)程

    Linux實(shí)現(xiàn)搭建ssh并允許使用root遠(yuǎn)程

    這篇文章主要介紹了Linux實(shí)現(xiàn)搭建ssh并允許使用root遠(yuǎn)程方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02

最新評論

腾冲县| 亚东县| 嘉禾县| 大城县| 辽阳市| 长武县| 社会| 曲麻莱县| 宁河县| 高雄县| 永善县| 镇赉县| 襄樊市| 墨竹工卡县| 衢州市| 华蓥市| 平谷区| 彭泽县| 威海市| 佛学| 南平市| 通许县| 垫江县| 九江市| 阜新| 庄河市| 广西| 息烽县| 蕉岭县| 禄丰县| 根河市| 改则县| 澄江县| 赣榆县| 册亨县| 辉县市| 山丹县| 庄浪县| 江西省| 萍乡市| 苗栗县|