精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深度解讀:C++線程池設計原理與應用實踐

開發 前端
C++ 作為一門強大的編程語言,其標準庫雖提供了多線程支持,但倘若直接使用std::thread進行大規模并發編程,線程創建與銷毀所帶來的開銷不容小覷。此時,線程池這一高效管理線程的機制應運而生。

C++ 作為一門強大的編程語言,其標準庫雖提供了多線程支持,但倘若直接使用std::thread進行大規模并發編程,線程創建與銷毀所帶來的開銷不容小覷。此時,線程池這一高效管理線程的機制應運而生。簡單來說,線程池是一種多線程處理形式,內部維護著一定數量的工作線程,并借助任務隊列管理執行任務。它就像一位智能管家,通過重用已有的線程,顯著減少了對象創建與銷毀的開銷,進而提升性能。

而且,當任務抵達時,無需等待線程創建即可立即執行,大大消除了延遲,讓應用程序響應更為迅速。此外,線程池還能對線程進行統一分配、調優與監控,極大地提高了線程的可管理性。接下來,讓我們深入探討 C++ 線程池的設計原理,結合實際案例展示其在不同場景中的應用實踐,一同領略線程池在提升程序性能與資源利用率方面的強大魅力 。

Part1.線程池是什么

1.1線程池概述

在并發編程的世界里,線程池是一種非常重要的多線程處理技術。它的核心思想就像是一個精心管理的工人團隊,在這個團隊里,預先創建了一定數量的線程,這些線程就如同待命的工人,時刻準備接受任務并執行。當有新的任務到來時,線程池不會像傳統方式那樣每次都去創建新的線程,而是直接從這個 “池子” 中挑選一個空閑的線程來處理任務。當任務執行完畢后,線程也不會被銷毀,而是重新回到線程池中,等待下一次任務的到來 ,就像工人完成一項工作后,不會離開團隊,而是繼續留在團隊里等待下一個工作安排。

為什么要使用這樣的方式呢?這是因為線程的創建和銷毀是比較 “昂貴” 的操作,會消耗一定的系統資源和時間。就好比每次有工作來臨時,都重新招聘和解雇工人,這不僅需要花費時間和精力去招聘、培訓新工人,還可能會因為頻繁的人員變動而影響工作效率。而線程池通過復用線程,就避免了這種頻繁創建和銷毀線程帶來的開銷,大大提高了系統的性能和資源利用率。同時,線程池還能夠有效地控制并發的線程數,避免因為線程過多而導致系統資源競爭激烈、上下文切換頻繁等問題,從而保證系統的穩定性和高效運行 。

圖片圖片

1.2為什么要用 C++ 線程池

在 C++ 編程中,多線程是提升程序性能和處理能力的重要手段。但是,如果每次有任務就創建新線程,任務完成就銷毀線程,會帶來諸多問題 。比如,線程創建和銷毀的過程涉及操作系統資源的分配與回收,這個過程需要消耗一定的時間和系統資源。想象一下,你要舉辦一場活動,每次有嘉賓來參加活動,你都要重新搭建一個活動場地,嘉賓離開后又馬上拆除場地,這顯然是非常低效且浪費資源的。在程序中,頻繁創建和銷毀線程就類似這種情況,會導致程序的運行效率降低,尤其是在處理大量短時間任務時,這種開銷可能會成為性能瓶頸。

線程數量過多也會占用大量系統資源,如內存、CPU 時間片等。過多的線程同時競爭這些資源,會導致上下文切換頻繁發生。上下文切換是指當 CPU 從一個線程切換到另一個線程執行時,需要保存當前線程的執行狀態,然后加載另一個線程的執行狀態,這個過程同樣會消耗 CPU 時間和系統資源。就像一個服務員要同時服務太多客人,不斷在不同客人之間來回切換,導致每個客人都不能得到及時有效的服務,程序也會因為頻繁的上下文切換而降低整體性能 。

使用 C++ 線程池,可以很好地解決這些問題。線程池通過預先創建一定數量的線程,讓這些線程復用,避免了頻繁的線程創建和銷毀開銷,就像提前搭建好一個固定的活動場地,所有嘉賓都在這個場地里活動,不需要每次都重新搭建和拆除。同時,線程池可以有效控制并發線程的數量,避免線程過多導致資源競爭和上下文切換的問題,保證系統資源的合理利用,讓程序能夠更加穩定、高效地運行 。

Part2.C++線程池的原理剖析

要深入理解 C++ 實現線程池的過程,首先得剖析其核心原理。線程池主要由幾個關鍵部分協同工作,包括線程隊列、任務隊列、互斥鎖、條件變量等,它們各自承擔著獨特的職責,共同構建起線程池高效運行的基礎 。

2.1線程隊列

線程隊列,就像是一個隨時待命的團隊,其中包含了預先創建好的多個線程。這些線程在創建后并不會立即執行具體的任務,而是進入一種等待狀態,隨時準備接受任務的分配。它們就像訓練有素的士兵,在軍營中等待著出征的命令。在 C++ 中,我們可以使用std::vector<std::thread>來創建和管理這個線程隊列。例如:

std::vector<std::thread> threads;
for (size_t i = 0; i < threadCount; ++i) {
    threads.emplace_back([this] { this->worker(); });
}

在這段代碼中,threadCount表示我們希望創建的線程數量,通過循環創建了threadCount個線程,并將它們添加到threads向量中。每個線程都執行worker函數,這個函數就是線程的工作邏輯所在。

任務隊列

任務隊列則是存儲待執行任務的地方,它像是一個任務倉庫。當有新的任務到來時,就會被添加到這個隊列中等待處理。任務隊列可以使用std::queue來實現,為了確保在多線程環境下的安全訪問,還需要配合互斥鎖和條件變量。比如:

std::queue<std::function<void()>> tasks;
std::mutex queueMutex;
std::condition_variable condition;

這里定義了一個tasks任務隊列,用于存儲類型為std::function<void()>的任務,也就是可以調用且無返回值的函數對象。queueMutex是互斥鎖,用于保護任務隊列,防止多個線程同時訪問導致數據不一致。condition是條件變量,用于線程間的同步,當有新任務添加到隊列時,通過條件變量通知等待的線程。

2.2互斥鎖

互斥鎖的作用至關重要,它就像一把鎖,用來保護共享資源,確保同一時間只有一個線程能夠訪問任務隊列。當一個線程想要訪問任務隊列(比如添加任務或取出任務)時,它必須先獲取互斥鎖。如果此時互斥鎖已經被其他線程持有,那么這個線程就會被阻塞,直到互斥鎖被釋放。在 C++ 中,使用std::mutex來實現互斥鎖,例如:

std::mutex mutex;
mutex.lock();
// 訪問任務隊列的代碼
mutex.unlock();

在這段代碼中,mutex.lock()用于獲取互斥鎖,當獲取到鎖后,就可以安全地訪問任務隊列。訪問完成后,通過mutex.unlock()釋放互斥鎖,讓其他線程有機會獲取鎖并訪問任務隊列。為了避免忘記解鎖導致死鎖,更推薦使用std::lock_guard或std::unique_lock,它們會在作用域結束時自動釋放鎖,例如:

{
    std::unique_lock<std::mutex> lock(mutex);
    // 訪問任務隊列的代碼
} // lock自動析構,釋放鎖

2.3條件變量

條件變量主要用于線程間的同步和通信。它與互斥鎖配合使用,當任務隊列中沒有任務時,工作線程可以通過條件變量進入等待狀態,釋放互斥鎖,讓出 CPU 資源。當有新任務添加到任務隊列時,就可以通過條件變量通知等待的線程,讓它們醒來并獲取互斥鎖,從任務隊列中取出任務執行。例如:

std::condition_variable condition;
std::unique_lock<std::mutex> lock(mutex);
while (tasks.empty()) {
    condition.wait(lock);
}
auto task = std::move(tasks.front());
tasks.pop();

在這段代碼中,condition.wait(lock)會使線程進入等待狀態,并釋放lock鎖。當其他線程調用condition.notify_one()或condition.notify_all()通知時,等待的線程會被喚醒,重新獲取lock鎖,然后繼續執行后續代碼,從任務隊列中取出任務。

2.4協同工作流程

線程池的工作流程是一個有序且高效的協作過程。當有新任務到來時,任務會被添加到任務隊列中。這個過程中,需要先獲取互斥鎖,以保證任務隊列的線程安全。添加任務后,通過條件變量通知等待的線程有新任務到來。

template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
    using return_type = typename std::result_of<F(Args...)>::type;
    auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");
        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return res;
}

在這段代碼中,enqueue函數用于將任務添加到任務隊列。首先,通過std::bind和std::make_shared創建一個包裝了任務的std::packaged_task,并獲取其對應的std::future用于獲取任務執行結果。然后,在臨界區內(通過std::unique_lock自動管理鎖)將任務添加到任務隊列tasks中。最后,通過condition.notify_one()通知一個等待的線程有新任務。

而工作線程在啟動后,會不斷地嘗試從任務隊列中獲取任務并執行。它們首先獲取互斥鎖,檢查任務隊列是否為空。如果為空,就通過條件變量等待,直到有新任務被添加。當獲取到任務后,線程會執行任務,執行完成后再次回到獲取任務的循環中。如果線程池停止,且任務隊列為空,線程就會退出。

void ThreadPool::worker() {
    while (true) {
        std::function<void()> task;
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            condition.wait(lock, [this] { return stop ||!tasks.empty(); });
            if (stop && tasks.empty()) return;
            task = std::move(tasks.front());
            tasks.pop();
        }
        task();
    }
}

在worker函數中,線程首先創建一個std::function<void()>類型的變量task用于存儲從任務隊列中取出的任務。然后,在臨界區內(通過std::unique_lock自動管理鎖),使用condition.wait等待條件滿足,條件是stop為true(表示線程池停止)或者任務隊列不為空。當條件滿足且stop為true且任務隊列為空時,線程返回退出。否則,從任務隊列中取出任務并移動到task中。最后,在臨界區外執行任務task()。這樣,通過線程隊列、任務隊列、互斥鎖和條件變量的緊密協作,線程池實現了高效的任務管理和并發執行 。

Part3.線程池實現方式

若線程池配置了 3 個線程,且任務隊列不設容量上限,其運行時會出現哪些典型情況?

具體來說,可能包括:當任務數量少于或等于 3 時,所有任務可被立即分配給線程執行;當任務數量超過 3 時,超出部分會進入隊列等待;若任務持續涌入且執行速度慢于提交速度,隊列會不斷膨脹;當所有任務執行完畢后,3 個線程會處于空閑狀態等待新任務;此外,還可能出現部分線程因任務阻塞而暫時閑置,其他線程仍在工作的情況。

3.1情況

①:無事可做,集體待命

線程池已啟動,三個工作線程全部就緒,但主線程尚未提交任何任務。此時任務隊列空無一人,三個線程只能原地阻塞等待,如同剛放長假時的你,一身力氣沒處使,只能閑著發呆。

圖片圖片

3.2情況

②:任務抵達,恰好分配

主線程忽然送來三個任務,像投遞包裹般接連放入隊列。三個線程隨即反應:“有任務了!” 立刻從等待狀態中蘇醒,依次取出任務。任務隊列轉眼被取空,三個線程各自帶著任務投入執行。此時主線程也毫無壓力,因為它提交的任務數量剛好能被線程池容納,不多不少。

圖片圖片

3.3情況

③:任務過剩,排隊等候

三個線程正全力處理任務時,主線程又新增了一個任務。由于此時線程池已無空閑線程,這個新任務只能進入隊列排隊等候。待三個線程中任意一個完成手頭工作,便會主動從隊列中取出下一個任務繼續執行。這正是線程池的 “先處理,后排隊” 機制,形成了 “線程等任務,任務等線程” 的循環。

圖片圖片

3.4情況

④:任務爆滿,主線程被迫停滯

這種情形較為極端,我們先假設任務隊列設有最大容量限制(例如最多只能存放 5 個任務)。此時線程池的三個線程都在忙碌,隊列也已處于滿員狀態。當主線程再想提交新任務時,會發現既沒有空閑位置可存放,也沒有線程能接手,只能原地等待,直到隊列中有任務被取走、騰出空位為止。

圖片圖片

Part4.C++實現線程池的步驟

4.1創建任務類

任務類在整個線程池體系中扮演著關鍵的角色,它主要負責封裝任務函數以及與之相關的參數,使得任務能夠以一種統一、規范的形式被線程池管理和調度。

在 C++ 中,我們可以通過以下方式來定義一個任務類:

class Task {
public:
    // 使用模板來接受任意可調用對象及其參數
    template<class F, class... Args>
    Task(F&& f, Args&&... args) : func(std::bind(std::forward<F>(f), std::forward<Args>(args)...)) {}

    // 定義任務的執行函數
    void execute() {
        if (func) {
            func();
        }
    }

private:
    std::function<void()> func;
};

在上述代碼中,我們利用了 C++ 的模板特性和std::function、std::bind來實現任務的封裝。std::function<void()>類型的成員變量func用于存儲可調用對象,通過std::bind將傳入的函數f和參數args綁定成一個無參的可調用對象,賦值給func。execute函數則是任務的執行入口,當調用execute時,會執行綁定好的函數。

例如,假設有一個簡單的加法函數:

int add(int a, int b) {
    return a + b;
}

我們可以創建一個任務對象來執行這個加法操作:

Task task(add, 3, 5);
task.execute(); // 執行任務,相當于調用add(3, 5)

這樣,通過任務類的封裝,我們可以將各種不同的任務以統一的方式進行管理和執行,為線程池的任務調度提供了基礎。

4.2構建線程池類

線程池類是整個線程池實現的核心部分,它負責管理線程隊列、任務隊列以及協調線程的工作。下面是線程池類的基本框架:

class ThreadPool {
public:
    // 構造函數,初始化線程池
    ThreadPool(size_t numThreads);

    // 析構函數,清理線程池資源
    ~ThreadPool();

    // 添加任務到任務隊列
    template<class F, class... Args>
    void enqueue(F&& f, Args&&... args);

private:
    // 線程執行的函數
    void worker();

    // 線程隊列
    std::vector<std::thread> threads;

    // 任務隊列
    std::queue<std::unique_ptr<Task>> tasks;

    // 互斥鎖,保護任務隊列
    std::mutex queueMutex;

    // 條件變量,用于線程同步
    std::condition_variable condition;

    // 線程池停止標志
    bool stop;
};

在這個線程池類中:

成員變量

threads是一個std::vector<std::thread>類型的線程隊列,用于存儲線程對象,每個線程都將執行worker函數。

tasks是一個std::queue<std::unique_ptr<Task>>類型的任務隊列,用于存儲任務對象,這里使用std::unique_ptr來管理任務對象的生命周期,確保內存安全。

queueMutex是一個互斥鎖,用于保護任務隊列,防止多個線程同時訪問任務隊列導致數據不一致。

condition是一個條件變量,與互斥鎖配合使用,用于線程間的同步。當任務隊列中沒有任務時,工作線程可以通過條件變量進入等待狀態,當有新任務添加到任務隊列時,通過條件變量通知等待的線程。

stop是一個布爾類型的標志,用于控制線程池的停止。當stop為true時,線程池將停止接受新任務,并在處理完現有任務后關閉所有線程。

構造函數

ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
    for (size_t i = 0; i < numThreads; ++i) {
        threads.emplace_back([this] { this->worker(); });
    }
}

構造函數接受一個參數numThreads,表示線程池中的線程數量。在構造函數中,通過循環創建numThreads個線程,并將它們添加到threads隊列中。每個線程都執行worker函數,[this] { this->worker(); }是一個 lambda 表達式,它捕獲了this指針,使得線程能夠訪問線程池類的成員函數和變量。

析構函數

ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        stop = true;
    }
    condition.notify_all();
    for (std::thread& thread : threads) {
        thread.join();
    }
}

析構函數用于清理線程池的資源。首先,在一個臨界區內(通過std::unique_lock自動管理鎖)將stop標志設置為true,表示線程池要停止。然后,通過condition.notify_all()通知所有等待的線程,讓它們有機會檢查stop標志并退出。最后,通過循環調用thread.join()等待所有線程執行完畢,釋放線程資源。

添加任務函數

template<class F, class... Args>
void ThreadPool::enqueue(F&& f, Args&&... args) {
    auto task = std::make_unique<Task>(std::forward<F>(f), std::forward<Args>(args)...);
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        if (stop) {
            throw std::runtime_error("enqueue on stopped ThreadPool");
        }
        tasks.push(std::move(task));
    }
    condition.notify_one();
}

enqueue函數是一個模板函數,用于將任務添加到任務隊列中。它接受一個可調用對象f和一系列參數args,通過std::make_unique創建一個Task對象,并將其添加到任務隊列tasks中。在添加任務時,先獲取互斥鎖,確保任務隊列的線程安全。如果線程池已經停止(stop為true),則拋出異常。添加任務后,釋放互斥鎖,并通過condition.notify_one()通知一個等待的線程有新任務到來。

4.3實現關鍵函數

在上述線程池類中,worker函數和enqueue函數是實現線程池功能的關鍵。

worker函數

void ThreadPool::worker() {
    while (true) {
        std::unique_ptr<Task> task;
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            condition.wait(lock, [this] { return stop ||!tasks.empty(); });
            if (stop && tasks.empty()) {
                return;
            }
            task = std::move(tasks.front());
            tasks.pop();
        }
        task->execute();
    }
}

worker函數是線程執行的主體函數。它在一個無限循環中運行,不斷嘗試從任務隊列中獲取任務并執行。

具體步驟如下:

  1. 首先創建一個std::unique_ptr<Task>類型的變量task,用于存儲從任務隊列中取出的任務。
  2. 使用std::unique_lock<std::mutex>來獲取互斥鎖,進入臨界區,保護任務隊列的訪問。
  3. 使用condition.wait等待條件滿足,條件是stop為true(表示線程池停止)或者任務隊列不為空。condition.wait會自動釋放互斥鎖,使線程進入等待狀態,直到被condition.notify_one()或condition.notify_all()喚醒。當線程被喚醒時,會重新獲取互斥鎖,繼續執行后續代碼。
  4. 檢查stop標志和任務隊列是否為空,如果stop為true且任務隊列為空,說明線程池已經停止且沒有任務了,此時線程返回,結束執行。
  5. 從任務隊列中取出第一個任務,并將其移動到task變量中,然后將任務從任務隊列中移除。
  6. 退出臨界區,釋放互斥鎖,執行任務的execute函數,完成任務的執行。
  7. 循環回到開始,繼續等待下一個任務。

enqueue函數

template<class F, class... Args>
void ThreadPool::enqueue(F&& f, Args&&... args) {
    auto task = std::make_unique<Task>(std::forward<F>(f), std::forward<Args>(args)...);
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        if (stop) {
            throw std::runtime_error("enqueue on stopped ThreadPool");
        }
        tasks.push(std::move(task));
    }
    condition.notify_one();
}

enqueue函數用于將任務添加到任務隊列中。具體步驟如下:

  1. 使用std::make_unique創建一個Task對象,將傳入的可調用對象f和參數args封裝到任務對象中。這里使用std::forward來實現完美轉發,確保參數的左值 / 右值特性不變。
  2. 使用std::unique_lock<std::mutex>獲取互斥鎖,進入臨界區,保護任務隊列的訪問。
  3. 檢查線程池是否已經停止,如果stop為true,說明線程池已經停止,此時拋出std::runtime_error異常,提示不能在停止的線程池中添加任務。
  4. 將創建好的任務對象通過std::move移動到任務隊列tasks中,std::move用于將一個對象的所有權轉移給另一個對象,避免不必要的拷貝。
  5. 退出臨界區,釋放互斥鎖。
  6. 通過condition.notify_one()通知一個等待的線程有新任務到來,被通知的線程會在condition.wait處被喚醒,然后嘗試從任務隊列中獲取任務并執行。

通過以上關鍵函數的實現,線程池能夠有效地管理線程和任務,實現任務的并發執行,提高程序的性能和效率。

Part5.線程池代碼示例與解析

5.1完整代碼展示

#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

class ThreadPool {
public:
    ThreadPool(size_t numThreads);
    ~ThreadPool();

    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;

private:
    void worker();

    std::vector<std::thread> threads;
    std::queue<std::function<void()>> tasks;

    std::mutex queueMutex;
    std::condition_variable condition;

    bool stop;
};


ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
    for (size_t i = 0; i < numThreads; ++i) {
        threads.emplace_back([this] {
            while (true) {
                std::function<void()> task;
                {
                    std::unique_lock<std::mutex> lock(queueMutex);
                    condition.wait(lock, [this] { return stop ||!tasks.empty(); });
                    if (stop && tasks.empty())
                        return;
                    task = std::move(tasks.front());
                    tasks.pop();
                }
                task();
            }
        });
    }
}


ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        stop = true;
    }
    condition.notify_all();
    for (std::thread& thread : threads) {
        thread.join();
    }
}


template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
    using return_type = typename std::result_of<F(Args...)>::type;
    auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        if (stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");
        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return res;
}

5.2代碼逐行解析

包含頭文件

#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

這些頭文件提供了實現線程池所需的各種工具和數據結構。vector用于存儲線程隊列,queue用于實現任務隊列,thread用于線程操作,mutex和condition_variable用于線程同步,functional用于處理可調用對象,future用于獲取異步任務的結果。

線程池類定義

class ThreadPool {
public:
    ThreadPool(size_t numThreads);
    ~ThreadPool();

    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;

private:
    void worker();

    std::vector<std::thread> threads;
    std::queue<std::function<void()>> tasks;

    std::mutex queueMutex;
    std::condition_variable condition;

    bool stop;
};

公有成員函數

  • ThreadPool(size_t numThreads):構造函數,用于初始化線程池,接受線程數量作為參數。
  • ~ThreadPool():析構函數,用于清理線程池資源,停止所有線程并等待它們結束。
  • template<class F, class... Args> auto enqueue(F&& f, Args&&... args) ->std::future<typename std::result_of<F(Args...)>::type>:模板函數,用于將任務添加到任務隊列中,并返回一個std::future對象,以便獲取任務的執行結果。

私有成員函數:void worker():線程執行的函數,每個線程都會調用這個函數,從任務隊列中獲取任務并執行。

私有成員變量

  • std::vector<std::thread> threads:線程隊列,存儲線程對象。
  • std::queue<std::function<void()>> tasks:任務隊列,存儲可調用對象,即任務。
  • std::mutex queueMutex:互斥鎖,用于保護任務隊列,確保線程安全。
  • std::condition_variable condition:條件變量,用于線程同步,當任務隊列有新任務時通知等待的線程。
  • bool stop:線程池停止標志,當stop為true時,線程池停止接受新任務,并在處理完現有任務后關閉所有線程。

構造函數實現

ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
    for (size_t i = 0; i < numThreads; ++i) {
        threads.emplace_back([this] {
            while (true) {
                std::function<void()> task;
                {
                    std::unique_lock<std::mutex> lock(queueMutex);
                    condition.wait(lock, [this] { return stop ||!tasks.empty(); });
                    if (stop && tasks.empty())
                        return;
                    task = std::move(tasks.front());
                    tasks.pop();
                }
                task();
            }
        });
    }
}
  • ThreadPool::ThreadPool(size_t numThreads) : stop(false):構造函數初始化列表,將stop標志初始化為false。
  • for (size_t i = 0; i < numThreads; ++i):循環創建numThreads個線程。
  • threads.emplace_back([this] {... });:使用emplace_back將新線程添加到threads隊列中,每個線程執行一個 lambda 表達式。
  • while (true):線程的主循環,不斷嘗試從任務隊列中獲取任務并執行。
  • std::function<void()> task;:定義一個變量task,用于存儲從任務隊列中取出的任務。
  • std::unique_lock<std::mutex> lock(queueMutex);:創建一個std::unique_lock對象,自動管理queueMutex鎖,進入臨界區。
  • condition.wait(lock, [this] { return stop ||!tasks.empty(); });:線程等待條件變量condition,當stop為true或者任務隊列不為空時,線程被喚醒。condition.wait會自動釋放lock鎖,使線程進入等待狀態,直到被通知喚醒,喚醒后會重新獲取lock鎖。
  • if (stop && tasks.empty()) return;:如果stop為true且任務隊列為空,說明線程池已經停止且沒有任務了,線程返回,結束執行。
  • task = std::move(tasks.front()); tasks.pop();:從任務隊列中取出第一個任務,并將其移動到task變量中,然后將任務從任務隊列中移除。
  • task();:執行任務。

析構函數實現

ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        stop = true;
    }
    condition.notify_all();
    for (std::thread& thread : threads) {
        thread.join();
    }
}
  • std::unique_lock<std::mutex> lock(queueMutex); stop = true;:在臨界區內,將stop標志設置為true,表示線程池要停止。
  • condition.notify_all();:通知所有等待的線程,讓它們有機會檢查stop標志并退出。
  • for (std::thread& thread : threads) { thread.join(); }:通過循環調用thread.join()等待所有線程執行完畢,釋放線程資源。

添加任務函數實現

template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
    using return_type = typename std::result_of<F(Args...)>::type;
    auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        if (stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");
        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return res;
}
  • using return_type = typename std::result_of<F(Args...)>::type;:使用std::result_of獲取函數F的返回值類型,并將其命名為return_type。
  • auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));:使用std::make_shared創建一個std::packaged_task對象,將傳入的函數f和參數args綁定在一起,并包裝成一個可調用對象,用于異步執行任務。std::forward用于完美轉發,確保參數的左值 / 右值特性不變。
  • std::future<return_type> res = task->get_future();:獲取std::packaged_task對象的std::future,用于獲取任務的執行結果。
  • std::unique_lock<std::mutex> lock(queueMutex);:創建一個std::unique_lock對象,自動管理queueMutex鎖,進入臨界區。
  • if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");:檢查線程池是否已經停止,如果stop為true,說明線程池已經停止,拋出std::runtime_error異常,提示不能在停止的線程池中添加任務。
  • tasks.emplace([task]() { (*task)(); });:將任務添加到任務隊列中,[task]() { (*task)(); }是一個 lambda 表達式,用于執行std::packaged_task對象。
  • condition.notify_one();:通知一個等待的線程有新任務到來,被通知的線程會在condition.wait處被喚醒,然后嘗試從任務隊列中獲取任務并執行。
  • return res;:返回std::future對象,以便調用者獲取任務的執行結果。

Part6.線程池的測試與優化

6.1測試線程池

為了驗證我們實現的線程池是否正確且穩定,編寫測試代碼是必不可少的環節。通過測試,我們可以檢查線程池的各項功能,如添加任務、執行任務、線程復用等是否符合預期。以下是一個簡單的測試示例:

#include <iostream>
#include <chrono>
#include <thread>
#include "ThreadPool.h"  // 假設線程池定義在這個頭文件中

// 定義一個簡單的任務函數
void simpleTask(int num) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Task " << num << " executed by thread " << std::this_thread::get_id() << std::endl;
}

int main() {
    ThreadPool pool(4);  // 創建一個包含4個線程的線程池

    // 添加多個任務到線程池
    for (int i = 0; i < 10; ++i) {
        pool.enqueue(simpleTask, i);
    }

    // 等待一段時間,讓任務有足夠時間執行
    std::this_thread::sleep_for(std::chrono::seconds(3));

    return 0;
}

在這個測試代碼中:

  • 首先定義了一個simpleTask函數,它接受一個整數參數num,在函數內部,線程會休眠 1 秒鐘,然后輸出任務編號和執行該任務的線程 ID。
  • 在main函數中,創建了一個包含 4 個線程的線程池pool。
  • 通過循環調用pool.enqueue方法,向線程池中添加 10 個任務,每個任務都執行simpleTask函數,并傳入不同的參數i。
  • 最后,主線程休眠 3 秒鐘,這是為了給線程池中的線程足夠的時間來執行任務。在實際應用中,可能需要更復雜的同步機制來確保所有任務都執行完畢。

運行這個測試代碼后,可以觀察輸出結果,確認每個任務是否都被正確執行,以及任務是否是由線程池中的不同線程執行的,從而驗證線程池的功能是否正常。

6.2性能優化

盡管我們已經實現了一個基本的線程池,但在實際應用中,還需要對其性能進行優化,以滿足不同場景的需求。下面分析一些常見的性能瓶頸,并提出相應的優化建議。

調整線程數量:線程池中的線程數量是一個關鍵參數,它直接影響著線程池的性能。如果線程數量過少,可能導致任務等待時間過長,無法充分利用系統資源;而線程數量過多,則會增加線程上下文切換的開銷,甚至可能導致系統資源耗盡。因此,需要根據任務的類型和系統的硬件配置來合理調整線程數量。

  • 對于 CPU 密集型任務:由于這類任務主要消耗 CPU 資源,過多的線程會導致頻繁的上下文切換,降低性能。一般來說,線程數量可以設置為 CPU 核心數或略小于 CPU 核心數。例如,在一個具有 4 個 CPU 核心的系統中,對于 CPU 密集型任務,線程池的線程數量可以設置為 4 或 3。
  • 對于 I/O 密集型任務:這類任務在執行過程中大部分時間都在等待 I/O 操作完成,CPU 利用率相對較低。因此,可以適當增加線程數量,以充分利用 CPU 資源。通常,線程數量可以設置為 CPU 核心數的 2 - 3 倍。比如,在同樣具有 4 個 CPU 核心的系統中,對于 I/O 密集型任務,線程池的線程數量可以設置為 8 - 12。

優化任務隊列的數據結構:任務隊列是線程池中的重要組成部分,其數據結構的選擇會影響任務的添加和獲取效率。在前面的實現中,我們使用了std::queue作為任務隊列,它是一個基于鏈表的隊列,在多線程環境下,鏈表的操作可能會帶來一定的性能開銷。

  • 可以考慮使用無鎖隊列:無鎖隊列利用原子操作來實現線程安全,避免了傳統鎖機制帶來的開銷,能夠提高任務隊列在高并發場景下的性能。例如,concurrent_queue是一個開源的無鎖隊列實現,它基于 CAS(Compare - And - Swap)操作,在多線程環境下具有較高的性能。
  • 根據任務特性選擇合適的隊列:如果任務具有優先級之分,可以使用優先級隊列(如std::priority_queue)來存儲任務,這樣可以確保高優先級的任務優先被執行。在一個實時系統中,可能會有一些緊急任務需要立即處理,使用優先級隊列就能滿足這種需求。

減少鎖的競爭:在多線程環境下,鎖的競爭是導致性能下降的一個重要因素。在線程池的實現中,互斥鎖用于保護任務隊列的訪問,當多個線程同時嘗試訪問任務隊列時,就會產生鎖競爭。

  • 使用細粒度鎖:可以將任務隊列按照一定的規則進行劃分,每個部分使用單獨的互斥鎖進行保護。這樣,不同的線程可以同時訪問不同部分的任務隊列,減少鎖的競爭。例如,將任務隊列按照任務類型劃分為多個子隊列,每個子隊列都有自己的互斥鎖。
  • 采用無鎖數據結構:除了前面提到的無鎖隊列,還可以使用其他無鎖數據結構來替代傳統的加鎖方式。例如,std::atomic類型可以用于實現一些簡單的無鎖數據結構,如原子計數器,它可以在多線程環境下高效地進行計數操作,而不需要使用鎖。

通過以上性能優化措施,可以顯著提升線程池的性能和效率,使其能夠更好地適應各種復雜的應用場景。

責任編輯:武曉燕 來源: 深度Linux
相關推薦

2025-02-24 08:00:00

線程池Java開發

2010-02-05 16:46:58

C++ TinyXml

2010-02-03 15:58:51

C++ timer

2010-02-04 09:33:08

C++指針重載

2017-05-04 16:33:58

Java線程池實踐

2024-05-06 00:00:00

ThreadPool線程調度

2010-02-05 17:58:32

C++鏈棧模板

2025-05-06 09:12:46

2025-01-14 00:10:00

Java應用程序

2020-03-05 15:34:16

線程池C語言局域網

2025-06-06 02:00:00

2025-03-25 10:29:52

2020-10-19 10:01:12

Nodejs線程池設計

2009-07-22 09:39:18

CLR線程池

2024-10-12 14:18:21

C++OOP函數重載

2021-05-26 11:30:24

Java線程池代碼

2024-10-18 16:58:26

2024-07-15 08:20:24

2015-11-30 11:14:59

C++對象池自動回收

2010-01-21 14:07:14

CC++聲明
點贊
收藏

51CTO技術棧公眾號

久久99精品久久久久久青青日本 | 久久久久久久久久久久久国产精品| 亚洲国产精品一| 亚洲女同在线| 日韩在线欧美在线国产在线| 精品伦一区二区三区| 亚洲国产福利| 亚洲免费观看高清完整 | 教室别恋欧美无删减版| 欧美日韩免费一区二区三区| 国产玉足脚交久久欧美| 黄色在线网站| 国产成人亚洲精品狼色在线| 国产999在线观看| av激情在线观看| 国产伦精品一区二区三区在线播放| 色一区在线观看| 免费看污污视频| 欧美偷拍视频| 国产suv精品一区二区883| 国产精品pans私拍| 国产无码精品久久久| 日韩大片在线播放| 亚洲精品国产美女| 亚洲综合日韩欧美| 成人欧美magnet| 一区二区在线观看视频| 亚洲欧美日韩综合一区| 天天爽夜夜爽夜夜爽| 国产一区二区三区观看| 国产精品久久久久久婷婷天堂| 国产网友自拍视频| 欧美一区综合| 精品久久久av| 国产精品综合激情| 精品国产一级毛片| 日韩电影大片中文字幕| 国产无套精品一区二区三区| 全球中文成人在线| 欧美在线观看18| 免费在线观看的毛片| 黄色软件视频在线观看| 亚洲一区欧美一区| 无码毛片aaa在线| 国产原创视频在线观看| 中文字幕免费一区| 日韩黄色影视| 国产九九在线| 亚洲国产岛国毛片在线| 色综合久久88色综合天天提莫| 五月婷婷狠狠干| 99免费精品视频| 狠狠干一区二区| 殴美一级特黄aaaaaa| 岛国一区二区在线观看| 成人欧美一区二区| 东京干手机福利视频| 处破女av一区二区| 国产精品乱子乱xxxx| 高潮一区二区三区乱码| 粉嫩久久99精品久久久久久夜| 97se国产在线视频| 国产91免费在线观看| 成人综合激情网| 成人蜜桃视频| 天堂在线中文字幕| 久久在线观看免费| 欧洲国产精品| 日日夜夜精品一区| 亚洲美腿欧美偷拍| 国产一区二区三区小说| 悠悠资源网亚洲青| 欧美中文字幕亚洲一区二区va在线| 亚洲精品乱码久久久久久自慰| 日韩美女在线看免费观看| 欧美手机在线视频| 超碰在线超碰在线| xxxxxhd亚洲人hd| 日韩av影视综合网| 91麻豆精品国产91久久综合| 午夜精品毛片| 久久久人成影片一区二区三区观看| 日韩大片免费在线观看| 久久久精品五月天| 成人av色在线观看| 欧美 日韩 中文字幕| 26uuu精品一区二区在线观看| 视频一区三区| 视频在线这里都是精品| 欧美日韩中文字幕综合视频| 亚洲最大成人在线观看| 亚洲大奶少妇| 亚洲欧美精品中文字幕在线| 日本中文在线视频| 日韩天天综合| 国产日本欧美一区二区三区在线| 亚洲产国偷v产偷v自拍涩爱| 久久午夜色播影院免费高清| 99热都是精品| 不卡一二三区| 日韩午夜电影在线观看| 国产精品无码午夜福利| 性xxxx欧美老肥妇牲乱| 欧洲亚洲妇女av| 国产高清免费在线观看| 久久久久久久电影| 少妇一晚三次一区二区三区| 成人va天堂| 亚洲高清一区二| 精品国产大片大片大片| 亚洲一区二区三区高清不卡| 亚洲字幕一区二区| 高清性色生活片在线观看| 亚洲最新视频在线观看| 一本岛在线视频| 亚洲色图丝袜| 国模gogo一区二区大胆私拍| 91美女精品网站| 久久婷婷成人综合色| 国产一级不卡视频| 麻豆久久久久| 亚洲另类欧美自拍| 国产性猛交普通话对白| 加勒比av一区二区| 日韩欧美一区二区三区四区| аⅴ资源天堂资源库在线| 3atv在线一区二区三区| 阿v天堂2014| 免费日韩一区二区| 成人综合色站| 青草视频在线免费直播 | 丰满少妇在线观看bd| 亚洲男人的天堂网| 最新av免费在线观看| 欧美精品乱码| 国产成人精品一区二区在线| 少妇激情av一区二区| 亚洲h在线观看| 秘密基地免费观看完整版中文| 影视一区二区| 成人激情在线播放| 国产网站在线免费观看| 欧美日韩另类国产亚洲欧美一级| 少妇人妻好深好紧精品无码| 久久亚洲色图| 日韩精品av一区二区三区| 欧美成人影院| 国产小视频国产精品| 天天干天天色综合| 久久精品欧美一区二区三区不卡| 国产乱子夫妻xx黑人xyx真爽| 日韩精品社区| 国产91|九色| 免费在线观看一级毛片| 欧美日韩在线视频观看| 人人妻人人藻人人爽欧美一区| 国产视频一区在线观看一区免费| 久久国产精品久久精品国产| 天堂在线中文网官网| 亚洲人成电影在线播放| 波多野结衣电车| 国产精品久久三| 久久无码人妻一区二区三区| 黄色在线一区| 久久久久久久有限公司| 毛片无码国产| 在线色欧美三级视频| 91在线观看喷潮| 亚洲欧美激情插| 国产伦精品一区二区三区精品| 国产欧美综合一区二区三区| 欧美最大成人综合网| 欧美网站免费| 欧美激情欧美激情| 日本成人一区二区三区| 欧美日韩一区中文字幕| www.色小姐com| 成人爱爱电影网址| 无人在线观看的免费高清视频| 国产精品伦理久久久久久| 99porn视频在线| 波多野结衣亚洲| xxx一区二区| 刘玥91精选国产在线观看| 日本高清不卡视频| 精品国产视频在线观看| av成人老司机| 中文字幕av专区| 在线成人h网| 亚洲国产日韩欧美| 一区二区三区亚洲变态调教大结局| 91po在线观看91精品国产性色 | 日韩福利视频一区| 国产精品高清在线| 青春草免费在线视频| 亚洲天堂2020| 亚洲黄色精品视频| 欧美日韩中文字幕一区二区| 精品少妇久久久久久888优播| 欧美国产一区二区| 精品无码人妻少妇久久久久久| 视频精品一区二区| 亚洲色欲久久久综合网东京热| 精品盗摄女厕tp美女嘘嘘| 国产精品久久久对白| 久久av影院| 91高清免费在线观看| www在线视频| 成人一区二区三区视频 | 久久精品国产精品亚洲综合| av在线播放天堂| 久久精品影视| 日韩在线三区| 欧美电影在线观看免费| 亚洲自拍偷拍色图| 国产69精品久久久久9999人| 国内精品小视频在线观看| 黄av在线免费观看| 国产性猛交xxxx免费看久久| 日韩一二三四| 亚洲国产高清高潮精品美女| 国产三级第一页| 欧美色国产精品| 亚洲欧美另类在线视频| 亚洲影视在线播放| 91嫩草|国产丨精品入口| 亚洲国产精品av| 中文字幕国产专区| 91色综合久久久久婷婷| 污污免费在线观看| 国产91丝袜在线播放| 天天操精品视频| 国内精品免费**视频| 欧美wwwwwww| 毛片av中文字幕一区二区| 男人透女人免费视频| 国产精品日韩久久久| 成人性生活视频免费看| 激情久久久久久久| a级黄色小视频| 亚洲精品视频啊美女在线直播| 996这里只有精品| 欧美全黄视频| 真实国产乱子伦对白视频| 中文字幕av亚洲精品一部二部| 综合久久国产| 亚洲精品网址| www国产免费| 红桃视频国产精品| 国产欧美日韩网站| 亚洲另类黄色| 无码人妻h动漫| 麻豆亚洲精品| 国产真人无码作爱视频免费| 中文日韩欧美| 91看片就是不一样| 日本免费新一区视频| 男女污污的视频| 麻豆国产精品一区二区三区| 99九九99九九九99九他书对| 紧缚捆绑精品一区二区| 亚洲AV成人精品| 不卡的av网站| 在线小视频你懂的| 中文字幕亚洲电影| 免费麻豆国产一区二区三区四区| 亚洲一卡二卡三卡四卡五卡| 国产尤物在线视频| 欧洲另类一二三四区| 一区二区三区免费观看视频| 91精品国产综合久久久久久久 | 中文字幕av资源| 欧美精品日韩综合在线| 亚洲第一免费视频| 日韩成人av一区| 岛国在线视频免费看| 最新69国产成人精品视频免费| caopen在线视频| 91精品国产91| 欧美激情三区| 精品无人区一区二区三区| 国产99久久| 久久久久久久免费视频| 午夜宅男久久久| 日韩大片一区二区| 成人av资源站| 成人精品一二三区| 午夜不卡av在线| 亚洲视频在线观看一区二区| 精品欧美乱码久久久久久1区2区| 毛片免费在线| 精品久久久av| 春暖花开亚洲一区二区三区| 91原创国产| 日韩在线高清| 怡红院av亚洲一区二区三区h| 日韩综合网站| 真人抽搐一进一出视频| 奇米精品一区二区三区在线观看| 日本wwww色| 欧美激情中文字幕一区二区| 国产无遮无挡120秒| 欧美性生活影院| 天堂v在线观看| 久久精品视频播放| 春暖花开亚洲一区二区三区| 国产福利久久| **女人18毛片一区二区| 岳毛多又紧做起爽| 国产69精品久久久久777| 99在线视频免费| 狠狠躁夜夜躁人人爽天天天天97 | 日韩av在线导航| 国产传媒在线播放| 国产精品91久久久| 欧美a一欧美| www污在线观看| 韩国v欧美v日本v亚洲v| 精品无码在线观看| 岛国av一区二区在线在线观看| 精品人妻无码一区二区| 色偷偷噜噜噜亚洲男人的天堂| 韩国成人漫画| 久久久久久欧美精品色一二三四| 亚洲天堂偷拍| 亚洲精品乱码久久久久久9色| 国产精品久久看| 国产一区二区视频网站| 日韩大陆毛片av| 国产精品蜜芽在线观看| 粉嫩高清一区二区三区精品视频 | 国产精品网友自拍| 青青青国产在线| 日韩国产一区三区| 波多野结衣久久| 99久久免费国| 亚洲视频免费| 美女露出粉嫩尿囗让男人桶| 亚洲精品综合在线| av免费在线观看不卡| 久久亚洲私人国产精品va| 男人天堂久久| 一区二区三区国| 久久se精品一区二区| 啪啪一区二区三区| 欧美电影影音先锋| 99福利在线| 91网免费观看| 国产一区亚洲| 亚洲色偷偷色噜噜狠狠99网| 大伊人狠狠躁夜夜躁av一区| 欧美日韩国产中文字幕在线| 日本高清久久天堂| 精品国产中文字幕第一页| 91香蕉视频污版| 国产精品久久久久久久第一福利 | 综合久久av| 久久久无码中文字幕久...| 国产精品一区专区| 免费三片在线播放| 亚洲成av人乱码色午夜| 手机av在线| 神马一区二区影院| 九一九一国产精品| 欧美高清视频一区二区三区| 精品国产精品网麻豆系列| 高端美女服务在线视频播放| 欧美成熟毛茸茸复古| 免费在线观看视频一区| 在线看的片片片免费| 欧美精品一区二区三区视频| 在线天堂新版最新版在线8| 天天综合色天天综合色hd| 国精产品一区一区三区mba桃花 | 欧美日韩高清在线| 伊人春色在线观看| 久久99蜜桃综合影院免费观看| 日韩经典中文字幕一区| 中国一级片在线观看| 欧美精品一区二区三区在线播放| 肉色欧美久久久久久久免费看| 在线国产精品网| 99在线精品观看| 最近中文字幕av| 欧美黑人巨大xxx极品| 国产精品一线天粉嫩av| 欧美视频亚洲图片| 欧美日韩国内自拍| 天堂中文8资源在线8| 国产三区精品| 麻豆精品新av中文字幕| 国产在线视频第一页| 在线观看欧美日韩国产| 亚洲视频一起| 亚洲老女人av| 午夜精品久久久久久| 日本黄色片在线观看| 精品在线不卡| 国产精品亚洲第一区在线暖暖韩国| 亚洲不卡视频在线观看|