C++解決方案:多線程同步經典案例之生產者消費者問題

來源:互聯網
上載者:User
抄自維基百科 :

生產者消費者問題(英語:Producer-consumer problem),也稱有限緩衝問題(英語:Bounded-buffer problem),是一個多線程同步問題的經典案例。該問題描述了共用固定大小緩衝區的兩個線程——即所謂的“生產者”和“消費者”——在實際運行時會發生的問題。生產者的主要作用是產生一定量的資料放到緩衝區中,然後重複此過程。與此同時,消費者也在緩衝區消耗這些資料。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入資料,消費者也不會在緩衝區中空時消耗資料。

要解決該問題,就必須讓生產者在緩衝區滿時休眠(要麼乾脆就放棄資料),等到下次消費者消耗緩衝區中的資料的時候,生產者才能被喚醒,開始往緩衝區添加資料。同樣,也可以讓消費者在緩衝區空時進入休眠,等到生產者往緩衝區添加資料之後,再喚醒消費者。

本文用一個ItemRepository類表示產品倉庫,其中包含一個數組和兩個座標表示的環形隊列、一個std::mutex成員、用來保證每次只被一個線程讀寫操作 (為了保證列印出來的訊息是一行一行的,在它閒置時候也借用的這個互斥量╮(╯▽╰)╭)、兩個std::condition_variable表示隊列不滿和不空的狀態,進而保證生產的時候不滿,消耗的時候不空。

#pragma once#include <chrono>//std::chrono#include <mutex>//std::mutex,std::unique_lock,std::lock_guard#include <thread>//std::thread#include <condition_variable>//std::condition_variable#include <iostream>//std::cout,std::endl#include <map>//std::mapnamespace MyProducerToConsumer {    static const int gRepositorySize = 10;//total size of the repository    static const int gItemNum = 97;//number of products to produce    std::mutex produce_mtx, consume_mtx;//mutex for all the producer thread or consumer thread    std::map<std::thread::id, int> threadPerformance;//records of every thread's producing/consuming number    struct ItemRepository {//repository class        int m_ItemBuffer[gRepositorySize];//Repository itself (as a circular queue)        int m_ProducePos;//rear position of circular queue        int m_ConsumePos;//head position of circular queue        std::mutex m_mtx;//mutex for operating the repository        std::condition_variable m_RepoUnfull;//indicating that this repository is unfull(then producers can produce items)        std::condition_variable m_RepoUnempty;//indicating that this repository is unempty(then consumers can produce items)    }gItemRepo;    void ProduceItem(ItemRepository *ir, int item) {        std::unique_lock <std::mutex>ulk(ir->m_mtx);        while ((ir->m_ProducePos + 1) % gRepositorySize == ir->m_ConsumePos) {//full(spare one slot for indicating)            std::cout << "Reposity is full. Waiting for consumers..." << std::endl;            ir->m_RepoUnfull.wait(ulk);//unlocking ulk and waiting for unfull condition        }        //when unfull        ir->m_ItemBuffer[ir->m_ProducePos++] = item;//procude and shift        std::cout << "Item No." << item << " produced successfully by "            <<std::this_thread::get_id()<<"!" << std::endl;        threadPerformance[std::this_thread::get_id()]++;        if (ir->m_ProducePos == gRepositorySize)//loop            ir->m_ProducePos = 0;        ir->m_RepoUnempty.notify_all();//item produced, so it's unempty; notify all consumers    }    int ConsumeItem(ItemRepository *ir) {        std::unique_lock<std::mutex>ulk(ir->m_mtx);        while (ir->m_ConsumePos == ir->m_ProducePos) {//empty            std::cout << "Repository is empty.Waiting for producing..." << std::endl;            ir->m_RepoUnempty.wait(ulk);        }        int item = ir->m_ItemBuffer[ir->m_ConsumePos++];        std::cout << "Item No." << item << " consumed successfully by "            <<std::this_thread::get_id()<<"!" << std::endl;        threadPerformance[std::this_thread::get_id()]++;        if (ir->m_ConsumePos == gRepositorySize)            ir->m_ConsumePos = 0;        ir->m_RepoUnfull.notify_all();//item consumed, so it's unempty; notify all consumers        return item;    }    void ProducerThread() {        static int produced = 0;//static variable to indicate the number of produced items        while (1) {            std::this_thread::sleep_for(std::chrono::milliseconds(10));//sleep long enough in case it runs too fast for other threads to procude            std::lock_guard<std::mutex>lck(produce_mtx);//auto unlock when break            produced++;            if (produced > gItemNum)break;            gItemRepo.m_mtx.lock();            std::cout << "Producing item No." << produced << "..." << std::endl;            gItemRepo.m_mtx.unlock();            ProduceItem(&gItemRepo, produced);        }        gItemRepo.m_mtx.lock();        std::cout << "Producer thread " << std::this_thread::get_id()            << " exited." << std::endl;        gItemRepo.m_mtx.unlock();    }    void ConsumerThread() {        static int consumed = 0;        while (1) {            std::this_thread::sleep_for(std::chrono::milliseconds(10));            std::lock_guard<std::mutex>lck(consume_mtx);            consumed++;            if (consumed > gItemNum)break;            gItemRepo.m_mtx.lock();            std::cout << "Consuming item available..." << std::endl;            gItemRepo.m_mtx.unlock();            ConsumeItem(&gItemRepo);        }        gItemRepo.m_mtx.lock();        std::cout << "Consumer thread " << std::this_thread::get_id()            << " exited." << std::endl;        gItemRepo.m_mtx.unlock();    }    void InitItemRepository(ItemRepository* ir) {        ir->m_ConsumePos = 0;        ir->m_ProducePos = 0;    }    void Run() {        InitItemRepository(&gItemRepo);        std::thread thdConsume[11];        std::thread thdProduce[11];        for (auto& t : thdConsume)t = std::thread(ConsumerThread);        for (auto& t : thdProduce)t = std::thread(ProducerThread);        for (auto& t : thdConsume)t.join();        for (auto& t : thdProduce)t.join();        for (auto& iter : threadPerformance)cout << iter.first << ":" << iter.second << endl;    }}

相關文章:

關於java生產者與消費者的執行個體詳解

java多線程之並發協作生產者消費者設計模式

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.