首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++11 實現半同步半異步線程池:架構設計與代碼詳解

     編輯:關於C++

在高併發場景下,若採用「一請求一線程」的傳統模式,頻繁創建銷毀線程會耗盡系統資源,上下文切換開銷也極大。線程池技術通過預創建固定數量線程、任務完成後重用線程,完美解決了這一問題。本文深入剖析半同步半異步線程池的三層架構,並提供基於 C++11 標準庫的完整實現代碼。

半同步半異步三層架構

半同步半異步模式將系統劃分為三個邏輯層次,實現任務接收與處理解耦:

1. 同步服務層

負責接收上層並發任務請求。請求到達時不直接處理,而是封裝為任務對象放入同步隊列,實現「快速接收、緩衝排隊」。

2. 同步排隊層

核心是一個線程安全的有界阻塞隊列。生產者(同步層)在隊列滿時阻塞等待;消費者(異步層)在隊列空時阻塞等待。該層起到流量削峰填谷、解耦生產消費速率的關鍵作用。

3. 異步服務層

由多個工作線程組成,並發從隊列取出任務執行。線程數通常設置為 CPU 核心數,最大化 CPU 利用率,避免過度上下文切換。

半同步半異步線程池架構圖

核心組件:線程安全同步隊列

同步隊列是線程池的心臟,基於 std::mutex、std::condition_variable 實現生產消費模型:

  • 雙條件變量:m_notEmpty 通知消費者有任務,m_notFull 通知生產者有空位
  • 完美轉發:支持左值/右值引用,避免不必要的拷貝
  • 優雅停止:原子標誌 m_needStop 配合 notify_all 喚醒所有等待線程退出
  • 批量取任務:Take(list) 一次移動整個隊列,減少鎖競爭,適合任務密集型場景

線程池核心邏輯

ThreadPool 類封裼了線程組管理與任務調度:

  • 構造函數:默認線程數為 hardware_concurrency(),自動適配硬件
  • 工作循環:RunInThread 持續批量取任務、逐個執行,m_running 原子標誌控制生命週期
  • 任務提交:AddTask 完美轉發任務到同步隊列
  • 安全停止:std::call_once 確保 StopThreadGroup 僅執行一次,join 等待所有線程退出

完整可編譯代碼示例

#include <list>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <functional>
#include <atomic>

using namespace std;

/******************************** 同步隊列 *******************************/
template <typename T>
class SyncQueue {
public:
    SyncQueue(int maxSize) : m_maxSize(maxSize), m_needStop(false) {}

    // 添加任務(左值/右值完美轉發)
    void Put(const T& x) { Add(x); }
    void Put(T&& x) { Add(std::forward<T>(x)); }

    // 批量取走所有任務
    void Take(std::list<T>& list) {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notEmpty.wait(locker, [this] { return m_needStop || NotEmpty(); });
        if (m_needStop) return;
        list = std::move(m_queue);
        m_notFull.notify_one();
    }

    // 取單個任務
    void Take(T& t) {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notEmpty.wait(locker, [this] { return m_needStop || NotEmpty(); });
        if (m_needStop) return;
        t = m_queue.front();
        m_queue.pop_front();
        m_notFull.notify_one();
    }

    void Stop() {
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            m_needStop = true;
        }
        m_notFull.notify_all();
        m_notEmpty.notify_all();
    }

    bool Empty() { std::lock_guard<std::mutex> locker(m_mutex); return m_queue.empty(); }
    bool Full()  { std::lock_guard<std::mutex> locker(m_mutex); return m_queue.size() == m_maxSize; }
    size_t Size() { std::lock_guard<std::mutex> locker(m_mutex); return m_queue.size(); }

private:
    bool NotFull() const {
        bool full = (m_queue.size() >= m_maxSize);
        if (full) cout << "the queue is full, need wait..." << endl;
        return !full;
    }
    bool NotEmpty() const {
        bool empty = m_queue.empty();
        if (empty) cout << "the queue is empty, need wait..., thread ID: " << this_thread::get_id() << endl;
        return !empty;
    }

    template <typename F>
    void Add(F&& x) {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notFull.wait(locker, [this] { return m_needStop || NotFull(); });
        if (m_needStop) return;
        m_queue.push_back(std::forward<F>(x));
        m_notEmpty.notify_one();
    }

private:
    std::list<T> m_queue;
    std::mutex m_mutex;
    std::condition_variable m_notEmpty;
    std::condition_variable m_notFull;
    int m_maxSize;
    bool m_needStop;
};

/************************** 線程池 *********************************/
const int MaxTaskCount = 100;

class ThreadPool {
public:
    using Task = std::function<void()>;

    ThreadPool(int numThreads = std::thread::hardware_concurrency()) : m_queue(MaxTaskCount) {
        cout << "numThreads: " << numThreads << endl;
        Start(numThreads);
    }
    ~ThreadPool() { Stop(); }

    void Stop() {
        std::call_once(m_flag, [this] { StopThreadGroup(); });
    }

    void AddTask(Task&& task) { m_queue.Put(std::forward<Task>(task)); }
    void AddTask(const Task& task) { m_queue.Put(task); }

private:
    void Start(int numThreads) {
        m_running = true;
        for (int i = 0; i < numThreads; ++i) {
            m_threadgroup.push_back(std::make_shared<std::thread>(&ThreadPool::RunInThread, this));
        }
    }

    void RunInThread() {
        while (m_running) {
            std::list<Task> list;
            m_queue.Take(list);
            for (auto& task : list) {
                if (!m_running) return;
                task();
            }
        }
    }

    void StopThreadGroup() {
        m_queue.Stop();
        m_running = false;
        for (auto& thread : m_threadgroup) {
            if (thread) thread->join();
        }
        m_threadgroup.clear();
    }

private:
    std::list<std::shared_ptr<std::thread>> m_threadgroup;
    SyncQueue<Task> m_queue;
    std::atomic_bool m_running{false};
    std::once_flag m_flag;
};

/************************** 測試主程序 *******************************/
int main() {
    ThreadPool pool;
    std::thread thd1([&pool] {
        for (int i = 0; i < 10; ++i) {
            auto thdId = this_thread::get_id();
            pool.AddTask([thdId] { cout << "1.thread id: " << thdId << endl; });
        }
    });
    std::thread thd2([&pool] {
        for (int i = 0; i < 10; ++i) {
            auto thdId = this_thread::get_id();
            pool.AddTask([thdId] { cout << "2.thread id: " << thdId << endl; });
        }
    });
    this_thread::sleep_for(std::chrono::seconds(2));
    getchar();
    pool.Stop();
    thd1.join();
    thd2.join();
    return 0;
}

線程池運行效果圖

補充:對象池模式

對象池適用於創建開銷大的對象(如數據庫連接、線程對象)。核心思想:預創建對象存入容器,使用時取出,歸還時放回,避免重複構造析構。

關鍵實現要點:

  • 繼承 NonCopyable 禁止拷貝
  • 使用 multimap<string, shared_ptr<T>> 按構造參數類型分類管理
  • 自定義 shared_ptr 刪除器:析構時不釋放內存,而是歸還對象池
  • typeid(Constructor).name() 作為鍵區分不同構造簽名
#include <string>
#include <functional>
#include <memory>
#include <map>
using namespace std;

class NonCopyable {
protected:
    NonCopyable() = default;
    ~NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

const int MaxObjectNum = 10;

template <typename T>
class ObjectPool : NonCopyable {
    template <typename... Args>
    using Constructor = function<shared_ptr<T>(Args...)>;

private:
    multimap<string, shared_ptr<T>> m_object_map;

public:
    template <typename... Args>
    void Init(size_t num, Args... args) {
        if (num <= 0 || num > MaxObjectNum) throw logic_error("Object num out of range");
        auto constructName = typeid(Constructor<Args...>).name();
        for (size_t i = 0; i < num; ++i) {
            m_object_map.emplace(constructName,
                shared_ptr<T>(new T(std::forward<Args>(args)...),
                    [this, constructName](T* p) {
                        cout << "dis: " << constructName << endl;
                        m_object_map.emplace(std::move(constructName), shared_ptr<T>(p));
                    }));
        }
    }

    template <typename... Args>
    shared_ptr<T> Get() {
        string constructName = typeid(Constructor<Args...>).name();
        auto range = m_object_map.equal_range(constructName);
        for (auto it = range.first; it != range.second; ++it) {
            auto ptr = it->second;
            m_object_map.erase(it);
            return ptr;
        }
        return nullptr;
    }
};
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved