任務隊列C++實現-(完美轉發)

来源:https://www.cnblogs.com/zuixime0515/archive/2023/10/31/17800370.html
-Advertisement-
Play Games

需求 任務隊列中可以依次添加任務; 任務執行函數需要接受外部傳輸的參數; 主動調用Start開始執行任務; 代碼實現 class TaskQueue { private: std::mutex mtx; std::condition_variable cv; std::queue<std::func ...


需求

  • 任務隊列中可以依次添加任務
  • 任務執行函數需要接受外部傳輸的參數
  • 主動調用Start開始執行任務

代碼實現

class TaskQueue {
private:
    std::mutex mtx;
    std::condition_variable cv;
    std::queue<std::function<void()>> task_queue;
    std::atomic<bool> is_running;

public:
    TaskQueue() : is_running(false) {}
    ~TaskQueue() {}

    // std::forward is used to forward the parameter to the function
    template<typename F, typename... Args>
    void Push(F&& f, Args&&... args) {
        std::lock_guard<std::mutex> lock(mtx);
        task_queue.push(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
        cv.notify_one();
    }

    void Start() {
        is_running = true;
        std::thread t([this] {
            while(is_running) {
                std::unique_lock<std::mutex> lock(mtx);
                cv.wait(lock, [this] { return !task_queue.empty(); });
                auto task = task_queue.front();
                task_queue.pop();
                lock.unlock();
                task();
            }
        });
        t.detach();
    }

    void Stop() {
        is_running = false;
    }
};
int main(int argc, char** argv) {

    TaskQueue tq;
    tq.Push(DoSomething, 1);
    tq.Push(DoSomething, 2);
    tq.Push(DoSomething, 3);
    tq.Start();
    tq.Push(DoSomething, 4);

    // 等待任務結束
    while(1) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    return 0;
}

實現筆記

任務隊列,將需要執行的任務存儲在隊列中,存儲的這個動作類似於生產者
當任務隊列不為空時,會從隊列中取出一個任務執行,當任務執行結束後再從隊列取下一個,直到隊列為空;
執行任務類似於消費者

基礎概念理解

  1. C++左值和右值

判斷表達式左值還是右值的兩種辦法:
a. 位於賦值符號=左側的就是左值,只能位於右側的就是右值;需要註意的是,左值也可以當右值用;
b. 有名稱、可以取到存儲地址的表達式就是左值,否則就是右值;

C++右值引用(用 &&標識)

  1. 和左值引用一樣,右值引用也需要立即被初始化,且只能使用右值進行初始化
int num = 10;
// 左值不能用於初始化右值
// int &&a = num; 編譯報錯
int &&a = 123;
  2. 和常量左值引用不同的是,右值引用可以對右值進行修改:
int num = 10;
int &&ref = 12;
ref = 222;// 修改右值引用的值
std::cout << ref << std::endl;
  1. std::unique_lock

std::unique_lock是個類模板,工作中,一般使用std::lock_guard(推薦使用) ,std::unique_lockstd::lock_guard靈活很多,效率上差一點,記憶體占用多一點。

  1. std::async 和 std::future

std::async是個函數模板,用來啟動一個非同步任務,啟動起來一個非同步任務之後(什麼叫“啟動一個非同步任務”,就是自動創建一個線程並開始執行對應的線程入口函數),他返回一個std::future對象,這個std::future對象裡面就含有線程函數返回的結果,我們可以通過調用std::future對象的成員函數get()來獲取結果;它返回一個std::future對象。

  1. 條件變數std::condition_variable

std:: condition_variable實際上是個類,是一個與條件相關的類,說白了就是等待一個條件的達成。這個類是需要和互斥量來配合工作的,用的時候我們要生成這個類的對象。
a. wait()

  1. 若第二個參數是true,wait()直接返回;
  2. 若第二個參數是Lambda表達式,且**返回值是false,wait()將解鎖互斥量,且在本行阻塞**。阻塞到何時結束呢?堵塞到其他線程調用notify_one() 為止;
  3. 若wait沒有第二個參數,則預設false;

b. notify_one()wait()的工作流程:
其他線程用notify_one()將本wait(原本是睡著/堵塞)的狀態喚醒後,wait就開始恢復幹活了,恢復後的wait乾什麼活

  1. wait不斷地嘗試重新獲取互斥量鎖,如果獲取不到,那麼流程就卡在wait這裡等著獲取,如果獲取到,那麼wait就繼續執行b
  2. 上鎖(實際上獲取到了鎖,等同於上鎖);
  3. 若wait有第二個參數,就判斷lambda的表達式值,若值為false,則wait又對互斥量解鎖,休眠;直到lambda值為true時,才會執行下一步;
  4. 為防止假喚醒,wait()中要有第二個參數(lambda)並且這個lambda中要正確處理公共數據是否存在;

完美轉發

定義:

函數模板可以將自己的參數“完美”地轉發給內部調用的其它函數。所謂完美,即不僅能準確地轉發參數的值,還能保證被轉發參數的左、右值屬性不變。

不管傳入的參數是什麼,都能夠很好的匹配函數需要的參數類型

C++11實現:

#include <iostream>
using namespace std;

// 接收左值
void ref_func(int& t) {
    cout << "lvalue\n";
}
void ref_func(const int& t) {
    cout << "rvalue\n";
}

//實現完美轉發的函數模板
template <typename T>
void function(T&& t) {
    ref_func(forward<T>(t));
}

int main()
{
    function(5);   // rvalue
    int  x = 1;
    function(x);   // lvalue
    return 0;
}

代碼中,重載的函數ref_func可以接收一個左值引用,也可以接收一個右值引用,但這需要定義兩個函數進行重載。為了實現形式的統一,定義了一個模板函數function,函數體內調用ref_func函數,該模板函數接收參數後,會將參數類型轉到具體的函數中進行調用。

完美轉發需要考慮的一些問題

  1. C++11規定,通常情況下右值引用形式的參數只能接收右值,不能接收左值;
  2. 對於函數模板中的使用右值引用語法定義的參數來說,上述規定不再有效。模板函數的右值引用參數既可以接收左值引用,也可以接收右值引用。此時的右值引用也被稱為萬能引用。
  3. 在實現完美轉發的時候,只要函數模板的參數類型為T&&,C++就可以自行準確判定實際傳入的實參是左值還是右值;
  4. 如何將函數模板接收到的形參,連同參數的左右值屬性,一切傳遞給被調用的函數呢?
    1. C++11為瞭解決這個問題,引入了std::forward()模板
//實現完美轉發的函數模板
template <typename T>
void function(T&& t) {
    // 將形參和其左右值屬性傳遞給被調用的函數
    ref_func(std::forward<T>(t));
}

隊列實現

  1. 添加任務的實現
    1. 需要將不同任務添加進隊列中,函數名可能不一樣,參數也不一樣
    2. 要求能夠添加不同的函數,執行不同的任務;

實現原理:
a. 類內定義一個隊列,元素是std::function<void()>,即std::function對象;
b. 使用一個模板函數,和完美轉發特性,將不同的函數添加進隊列中;

template<typename F, typename... Args>
void Push(F&& f, Args&&... args) {
    std::lock_guard<std::mutex> lock(mtx);
    task_queue.push(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
    cv.notify_one();
}

Push函數中使用了std::bind類模板,將傳入函數f和其需要的參數綁定在一起,生成一個std::function類對象,

往隊列中添加完任務之後,則需要通過條件變數cv通知消費者可以進行消費

  1. 按序執行任務,需要從隊列中一個個取出來執行,
void Start() {
    is_running = true;
    std::thread t([this] {
        while(is_running) {
            std::unique_lock<std::mutex> lock(mtx);
            cv.wait(lock, [this] { return !task_queue.empty(); });
            auto task = task_queue.front();
            task_queue.pop();
            lock.unlock();
            task();
        }
    });
    t.detach();
}
這裡將創建的執行任務線程用detach方法放在後臺執行,

​ 這裡將創建的執行任務線程用detach方法放在後臺執行,當隊列中沒有任務可以執行的之後,將會等待隊列中有任務時在執行,將一直阻塞在cv.wait(lock, [this] { return !task_queue.empty(); });中。

使用說明

  1. 先生成一個任務隊列的對象;

  2. 調用Push將需要執行的函數和參數加到隊列中;

  3. 調用Start介面,讓任務按序執行;

拓展:

  1. 如果要等任務結束後在執行下一個任務,則需要在task()後面加上一個條件變數,等待任務結束在取下一個任務;
  2. 若要讓執行任務的線程一開始就運行,則可以將Start函數放在構造函數中;

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、算數運算符 算術運算符(+,-, *,/,%【重要】,++【重要】,--) 其中 + 的左邊和右邊有 " ", 表示拼接 i++,表示先賦值後+1 ++i ,表示先+1後賦值 同理減法也是如此 算數運算中,+ 可以進行隱式迭代,將字元串數字轉化為Number類型 alert(typeof(+'5 ...
  • 為了廣泛支持營銷活動的複雜與靈活,Wonder8.promotion(旺德發.營銷)引擎使用專門設計的表達式高度提煉信息,可以輕鬆表達營銷活動與用戶選取的商品組合之間匹配範圍、要求、折扣方式,可以設定多條營銷規則的邏輯聯合、分組、優先順序,並且支持多種為用戶計算最優折扣的策略。 本引擎功能細節較多,建... ...
  • 一、定義 為子系統中的一組介面提供一個一致的入口,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。外觀模式是一種結構型模式。 二、描述 包含以下兩個角色:1、Facade(外觀角色):在客戶端可以調用它的方法,在外觀角色中可以知道相關的(一個或多個)子系統的功能和責任;在正常情況下, ...
  • 今天是阿裡雲棲大會的第一天,相信場外的瓜,大家都吃過了。這裡就不說了,有興趣可以看看這裡:雲棲大會變成相親現場,最新招婿鄙視鏈來了... 。 這裡主要說說阿裡還發佈了一款AI編碼助手,對於我們開發者來說,還是非常值得關註的。 根據官網介紹,這款插件支持 VS Code、JetBrains 旗下的諸多 ...
  • Python 提供了一組內置的數學函數,包括一個廣泛的數學模塊,可以讓您對數字執行數學任務。 內置數學函數。min() 和 max() 函數可用於在可迭代對象中查找最低或最高值: 示例:查找可迭代對象中的最低或最高值: x = min(5, 10, 25) y = max(5, 10, 25) pr ...
  • 來源:同程藝龍 會員系統是一種基礎系統,跟公司所有業務線的下單主流程密切相關。如果會員系統出故障,會導致用戶無法下單,影響範圍是全公司所有業務線。所以,會員系統必須保證高性能、高可用,提供穩定、高效的基礎服務。 隨著同程和藝龍兩家公司的合併,越來越多的系統需要打通同程 APP、藝龍 APP、同程微信 ...
  • 每當一個網站崩潰,在座的各位都有一定的責任。 當一個爬蟲教程不火的時候還好,火起來了,就到了考驗網站伺服器的時候了,上一次茶杯狐就是這樣,還好人家頑強… 好了話不多說,直接開始。 首先就是必備的軟體和模塊 環境使用 Python 3.8 Pycharm 模塊使用 requests --> pip i ...
  • Java9及以後的版本引入了模塊化特性,實際實踐了一段時間之後發現“真香!”現在把“利用Java模塊化精簡JRE”的方法和經驗分享給大家。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...