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

利用C++如何實現(xiàn)一個阻塞隊列詳解

 更新時間:2020年10月04日 09:45:10   作者:進擊的小嘍啰  
這篇文章主要給大家介紹了關(guān)于利用C++如何實現(xiàn)一個阻塞隊列的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

阻塞隊列是多線程中常用的數(shù)據(jù)結(jié)構(gòu),對于實現(xiàn)多線程之間的數(shù)據(jù)交換、同步等有很大作用。

阻塞隊列常用于生產(chǎn)者和消費者的場景,生產(chǎn)者是向隊列里添加元素的線程,消費者是從隊列里取元素的線程。簡而言之,阻塞隊列是生產(chǎn)者用來存放元素、消費者獲取元素的容器。

考慮下,這樣一個多線程模型,程序有一個主線程 master 和一些 worker 線程,master 線程負(fù)責(zé)接收到數(shù)據(jù),給 worker 線程分配數(shù)據(jù),worker 線程取得一個任務(wù)后便可以開始工作,如果沒有任務(wù)便阻塞住,節(jié)約 cpu 資源。

  • master 線程  (生產(chǎn)者):負(fù)責(zé)往阻塞隊列中塞入數(shù)據(jù),并喚醒正在阻塞的 worker 線程。
  • workder 線程(消費者):負(fù)責(zé)從阻塞隊列中取數(shù)據(jù),如果沒有數(shù)據(jù)便阻塞,直到被 master 線程喚醒。

那么怎樣的數(shù)據(jù)結(jié)構(gòu)比較適合做這樣的喚醒呢?顯而易見,是條件變量,在 c++ 11 中,stl 已經(jīng)引入了線程支持庫。

C++11 中條件變量

條件變量一般與一個 互斥量 同時使用,使用時需要先給互斥量上鎖,然后條件變量會檢測是否滿足條件,如果不滿足條件便會暫時釋放鎖,然后阻塞線程。

c++ 11使用方法主要如下:

#include <mutex>
#include <condition_value>
// 互斥量與條件變量
std::mutex m_mutex;
std::condition_value m_condition;

// 請求信號的一方
std::unique_lock<std::mutex> lock(mutex);
while(xxx)
{
 // 這里會先釋放 lock,
 // 如果有信號喚醒的話,會重新加鎖。
 m_condition.wait(lock);
}

// 發(fā)送消息進行同步的一方
{
 std::unique_lock<std::mutex> lock(mutex);
 // 喚醒其他正在 wait 的線程
 m_condition.notify_all();
}

用 C++11 實現(xiàn)阻塞隊列

我們使用條件變量包裝 STL 中的 queue 就可以實現(xiàn)阻塞隊列功能,如果有興趣,甚至可以自己實現(xiàn)一個效率更高的隊列數(shù)據(jù)結(jié)構(gòu)。

我們先假設(shè)一下阻塞隊列需要如下接口:

  • push 將一個變量塞入隊列;
  • take 從隊列中取出一個元素;
  • size 查看隊列有多少個元素;
template <typename T>
class BlockingQueue
{
public:
 BlockingQueue();
 void push(T&& value);
 T take();
 size_t size() const;
 
private:
 // 實際使用的數(shù)據(jù)結(jié)構(gòu)隊列
 std::queue<T> m_data;

 // 條件變量
 std::mutex m_mutex;
 std::condition_variable m_condition;
};

push 一個變量時,我們需要先加鎖,加鎖成功后才可以壓入變量,這是為了線程安全。壓入變量后,就可以發(fā)送信號通知正在阻塞的條件變量。

 void push(T&& value)
 {
  // 往隊列中塞數(shù)據(jù)前要先加鎖
  std::unique_lock<std::mutex> lock(m_mutex);
  m_data.push(value);
  // 喚醒正在阻塞的條件變量
  m_condition.notify_all();
 }

take 一個變量時,就要有些不一樣:

  1. 先加鎖,加鎖成功后,如果隊列不為空,可以直接取數(shù)據(jù),不需要 wait;
  2. 如果隊列為空呢,則 wait 等待,直到被喚醒;
  3. 考慮特殊情況,喚醒后隊列依然是空的……
 T take()
 {
  std::unique_lock<std::mutex> lock(m_mutex);
  while(m_data.empty())
  {
   // 等待
   m_condition.wait(lock);
  }
  assert(!m_data.empty());
  T value(std::move(m_data.front()));
  m_data.pop();

  return value;
 }

總結(jié)下,代碼如下:

#ifndef BLOCKINGQUEUE_H
#define BLOCKINGQUEUE_H

#include <queue>
#include <mutex>
#include <condition_variable>
#include <assert.h>

template <typename T>
class BlockingQueue
{
public:
 BlockingQueue()
  :m_mutex(),
   m_condition(),
   m_data()
 {
 }

 // 禁止拷貝構(gòu)造
 BlockingQueue(BlockingQueue&) = delete;

 ~BlockingQueue()
 {
 }

 void push(T&& value)
 {
  // 往隊列中塞數(shù)據(jù)前要先加鎖
  std::unique_lock<std::mutex> lock(m_mutex);
  m_data.push(value);
  m_condition.notify_all();
 }

 void push(const T& value)
 {
  std::unique_lock<std::mutex> lock(m_mutex);
  m_data.push(value);
  m_condition.notify_all();
 }

 T take()
 {
  std::unique_lock<std::mutex> lock(m_mutex);
  while(m_data.empty())
  {
   m_condition.wait(lock);
  }
  assert(!m_data.empty());
  T value(std::move(m_data.front()));
  m_data.pop();

  return value;
 }

 size_t size() const
 {
  std::unique_lock<std::mutex> lock(m_mutex);
  return m_data.size();
 }
private:
 // 實際使用的數(shù)據(jù)結(jié)構(gòu)隊列
 std::queue<T> m_data;

 // 條件變量的鎖
 std::mutex m_mutex;
 std::condition_variable m_condition;
};
#endif // BLOCKINGQUEUE_H

實驗代碼

我們寫個簡單的程序?qū)嶒炓幌?,下面程序?一個 master 線程,5個 worker 線程,master線程生成一個隨機數(shù),求 0-隨機數(shù) 的和。

#include <iostream>
#include <thread>
#include <mutex>
#include <random>

#include <windows.h>

#include <blockingqueue.h>
using namespace std;

int task=100;
BlockingQueue<int> blockingqueue;
std::mutex mutex_cout;

void worker()
{
 int value;
 thread::id this_id = this_thread::get_id();
 while(true)
 {
  value = blockingqueue.take();
  uint64_t sum = 0;
  for(int i = 0; i < value; i++)
  {
   sum += i;
  }

  // 模擬耗時操作
  Sleep(100);

  std::lock_guard<mutex> lock(mutex_cout);
  std::cout << "workder: " << this_id << " "
     << __FUNCTION__
     << " line: " << __LINE__
     << " sum: " << sum
     << std::endl;
 }
}

void master()
{
 srand(time(nullptr));
 for(int i = 0; i < task; i++)
 {
  blockingqueue.push(rand()%10000);
  printf("%s %d %i\n",__FUNCTION__, __LINE__, i);
  Sleep(20);
 }
}

int main()
{
 thread th_master(master);
 std::vector<thread> th_workers;
 for(int i =0; i < 5; i++)
 {
  th_workers.emplace_back(thread(worker));
 }

 th_master.join();
 return 0;
}

從輸出結(jié)果可以看出,master 線程將任務(wù)分配給了正在空閑的 worker 線程,具體是哪個線程就看操作系統(tǒng)的隨機調(diào)度了。

master 46 5
worker: 3 worker line: 34 sum: 20998440
master 46 6
worker: 7 worker line: 34 sum: 3308878
master 46 7
worker: 4 worker line: 34 sum: 34598721
master 46 8
worker: 6 worker line: 34 sum: 1563796
master 46 9
worker: 5 worker line: 34 sum: 27978940

Reference

條件變量

總結(jié)

到此這篇關(guān)于利用C++如何實現(xiàn)一個阻塞隊列的文章就介紹到這了,更多相關(guān)C++實現(xiàn)阻塞隊列內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言中函數(shù)棧幀的創(chuàng)建和銷毀的深層分析

    C語言中函數(shù)棧幀的創(chuàng)建和銷毀的深層分析

    在C語言中,每一個正在運行的函數(shù)都有一個棧幀與其對應(yīng),棧幀中存儲的是該函數(shù)的返回地址和局部變量。從邏輯上講,棧幀就是一個函數(shù)執(zhí)行的環(huán)境:函數(shù)參數(shù)、函數(shù)的局部變量、函數(shù)執(zhí)行完后返回到哪里等等
    2022-04-04
  • C++日期類實現(xiàn)的完整操作

    C++日期類實現(xiàn)的完整操作

    C++標(biāo)準(zhǔn)庫沒有提供所謂的日期類型,C++繼承了C語言用于日期和時間操作的結(jié)構(gòu)和函數(shù),這篇文章主要給大家介紹了關(guān)于C++日期類實現(xiàn)的完整操作,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • C/C++左旋字符串實現(xiàn)代碼舉例

    C/C++左旋字符串實現(xiàn)代碼舉例

    在C/C++語言中沒有專門的字符串變量,通常用字符數(shù)組來存放字符串,下面這篇文章主要給大家介紹了關(guān)于C/C++左旋字符串實現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • C++ 面向?qū)ο蟪绦蛟O(shè)計--內(nèi)存分區(qū)詳解

    C++ 面向?qū)ο蟪绦蛟O(shè)計--內(nèi)存分區(qū)詳解

    這篇文章主要介紹了剖析C++的面向?qū)ο缶幊趟枷?C++的面向?qū)ο筇匦允瞧鋵語言的重要拓展之處,需要的朋友可以參考下,希望能夠給你帶來幫助
    2021-08-08
  • 基于C語言實現(xiàn)UDP客戶端

    基于C語言實現(xiàn)UDP客戶端

    UDP是一種面向無連接的傳輸層協(xié)議,廣泛應(yīng)用于實時性要求較高的場景,本文將介紹如何使用C語言實現(xiàn)一個簡單的UDP客戶端程序,有需要的可以參考下
    2024-10-10
  • C++深入探究繼承的概念與使用

    C++深入探究繼承的概念與使用

    繼承是C++面向?qū)ο缶幊讨械囊婚T。繼承是子類繼承父類的特征和行為,或者是繼承父類得方法,使的子類具有父類得的特性和行為。重寫是子類對父類的允許訪問的方法實行的過程進行重新編寫,返回值和形參都不能改變。就是對原本的父類進行重新編寫,但是外部接口不能被重寫
    2022-05-05
  • C語言掃雷游戲的實現(xiàn)方法

    C語言掃雷游戲的實現(xiàn)方法

    這篇文章主要為大家詳細(xì)介紹了C語言掃雷游戲的實現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • QT網(wǎng)絡(luò)編程Tcp下C/S架構(gòu)的即時通信實例

    QT網(wǎng)絡(luò)編程Tcp下C/S架構(gòu)的即時通信實例

    下面小編就為大家?guī)硪黄猀T網(wǎng)絡(luò)編程Tcp下C/S架構(gòu)的即時通信實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • C++可變參數(shù)函數(shù)的實現(xiàn)方法示例

    C++可變參數(shù)函數(shù)的實現(xiàn)方法示例

    這篇文章主要給大家介紹了關(guān)于C++可變參數(shù)函數(shù)的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • C語言實現(xiàn)Linux下的socket文件傳輸實例

    C語言實現(xiàn)Linux下的socket文件傳輸實例

    這篇文章主要介紹了C語言實現(xiàn)Linux下的socket文件傳輸?shù)姆椒?較為詳細(xì)的分析了C語言文件Socket文件傳輸客戶端與服務(wù)器端相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2015-06-06

最新評論

长岛县| 彩票| 郁南县| 浦江县| 措美县| 佛学| 松桃| 邯郸县| 琼中| 昔阳县| 梅河口市| 佛山市| 普宁市| 祁东县| 鹤庆县| 福贡县| 仁怀市| 北流市| 固始县| 余姚市| 康保县| 大足县| 瑞安市| 绿春县| 阳朔县| 阿鲁科尔沁旗| 周至县| 中西区| 石台县| 大宁县| 唐河县| 元江| 江永县| 南郑县| 扎鲁特旗| 铜川市| 英超| 于都县| 日喀则市| 鄱阳县| 宁津县|