精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

韩国av免费观看| 色18美女社区| 第九色区av在线| 免费成人小视频| 欧美日韩成人免费| 中文字幕在线免费看线人 | 日韩电影在线观看完整免费观看| 色悠悠久久综合| 国产高清免费在线| 欧美日韩激情视频一区二区三区| 黄页视频在线91| 7m精品福利视频导航| 在线观看天堂av| 麻豆成人入口| 91超碰这里只有精品国产| 僵尸世界大战2 在线播放| 日本中文字幕在线2020| 99热在这里有精品免费| 国产情人节一区| 亚洲天堂日韩av| 中文字幕人成人乱码| 亚洲男人天堂2024| 亚洲麻豆一区二区三区| 久久三级毛片| 色综合久久久久综合体| 欧美中日韩在线| 老司机午夜在线| 国产日韩欧美a| 精品国产中文字幕| 国产wwwxxx| 久久精品国产77777蜜臀| 国语自产精品视频在免费| 亚洲波多野结衣| 俺要去色综合狠狠| 日韩精品免费在线视频观看| 在线观看欧美一区二区| 四虎国产精品免费久久5151| 欧美性受xxxx黑人xyx性爽| 国产一区二区三区精彩视频| 国产蜜臀av在线播放| 国产精品精品国产色婷婷| 神马一区二区影院| 日本成人一区| 91玉足脚交白嫩脚丫在线播放| 99久久综合狠狠综合久久止| 国产婷婷在线视频| 精品一区二区三区久久久| 国产精品欧美一区二区| 中文字幕丰满人伦在线| 日韩在线观看一区二区| 日本久久久久久久久久久| 国产精品自拍99| 亚洲视频www| 国内精品一区二区三区| 久久精品视频9| 日韩天堂av| 57pao国产成人免费| 国产成人在线视频观看| 亚洲在线日韩| 日本久久久久久久久久久| www毛片com| 日韩福利视频导航| 国产精品老女人视频| 一区二区视频网站| 国产一区美女在线| 成人三级视频在线观看一区二区| 亚洲AV午夜精品| 99精品视频在线播放观看| 久久精品国产综合精品| 九九热视频在线观看| 亚洲国产电影在线观看| 中文字幕在线亚洲三区| 日本动漫理论片在线观看网站| 一区二区三区在线视频免费观看| 欧美精品卡一卡二| 特黄毛片在线观看| 欧美午夜精品一区二区蜜桃| www.久久久久久久久久久| 久久av偷拍| 亚洲精品99久久久久中文字幕| 老鸭窝一区二区| 成人婷婷网色偷偷亚洲男人的天堂| 日韩中文有码在线视频| 欧美日韩大片在线观看| 在线亚洲一区| 国产欧美日韩精品丝袜高跟鞋| 国产视频一区二区三| 成人午夜精品一区二区三区| 欧洲av一区| 伊人福利在线| 精品久久久视频| 亚洲污视频在线观看| 精品国产一区二区三区性色av| 亚洲国产欧美一区二区丝袜黑人 | 亚洲一区二区精品视频| 99爱视频在线| 国产成年精品| 国产视频精品在线| 午夜免费激情视频| 久久久久99| 操一操视频一区| 国产中文字幕在线观看| 亚洲一区二区三区四区中文字幕| 日韩视频在线免费看| 久久久久久亚洲精品美女| 亚洲美女动态图120秒| 久久久久亚洲av片无码| 久久精品一区二区国产| 亚洲xxxx在线| yiren22综合网成人| 亚洲成人1区2区| 日韩av一卡二卡三卡| 竹菊久久久久久久| 欧美国产日韩一区| 91 中文字幕| 国产无人区一区二区三区| 韩国无码av片在线观看网站| 欧美成人毛片| 国产亚洲精品久久久| 国产精品18p| 国产在线日韩欧美| 日韩福利一区二区三区| 成人免费网站观看| 日韩欧美一级二级三级| 国产精品18在线| 麻豆精品网站| 精品久久久久久亚洲| 伊人精品影院| 欧美一级二级三级蜜桃| 国产黄色录像片| 免费日本视频一区| 日本午夜精品一区二区三区| 人人草在线视频| 亚洲电影免费观看高清完整版在线观看| 91ts人妖另类精品系列| 青青草国产成人99久久| 日韩精品国内| 美女网站视频一区| 一区二区三区四区视频| 无码aⅴ精品一区二区三区| 26uuu国产在线精品一区二区| 18禁裸男晨勃露j毛免费观看| 6080亚洲理论片在线观看| 欧美成人三级视频网站| 国产手机视频在线| 亚洲图片你懂的| www.色就是色.com| 国产精品7m凸凹视频分类| 成人xvideos免费视频| 91在线导航| 欧美精选午夜久久久乱码6080| 亚洲精品天堂网| 久久99精品久久久| 亚洲啊啊啊啊啊| 一区二区三区亚洲变态调教大结局| 久久国产精品久久久久| 国产av无码专区亚洲a∨毛片| 97在线精品| 中文字幕va一区二区三区| 欧美 日本 亚洲| 女厕嘘嘘一区二区在线播放 | 不卡av在线网站| 国产三级视频在线播放| 一区二区三区四区中文字幕| 日韩精品xxx| 99热免费精品| 日本在线播放不卡| 欧美啪啪网站| 欧美男插女视频| 天堂中文在线资源| 色综合久久中文字幕| 日本成人免费在线观看| 国产在线播精品第三| 国产一区二区三区乱码| 视频小说一区二区| 国产精品视频成人| 欧美日韩在线视频免费观看| 精品奇米国产一区二区三区| 国产污污视频在线观看| 国产欧美1区2区3区| 亚欧精品在线视频| 一本久久综合| 中文字幕制服丝袜在线| 国产日韩三级| 国产精品视频区1| 日韩激情美女| 亚洲性视频网址| 国产日韩精品suv| 精品国产老师黑色丝袜高跟鞋| 国产熟女一区二区| 国产成人精品亚洲午夜麻豆| 九九九九免费视频| 91精品国产自产在线观看永久∴| 亚洲视频成人| 日本午夜精品一区二区| 日韩精品成人| 国产精品国产自产拍高清av水多| av黄在线观看| 亚洲欧美日韩图片| 精品人妻无码一区二区| 色综合咪咪久久| 国产亚洲精品久久777777| 国产亚洲欧美中文| 久久久久国产免费| 免费高清在线一区| jizzjizz国产精品喷水| 性欧美69xoxoxoxo| 日本不卡在线播放| 好吊妞视频这里有精品| 国产日韩欧美日韩| 625成人欧美午夜电影| 欧美成aaa人片免费看| 成人欧美一区| 日韩国产激情在线| 国产91麻豆视频| 91麻豆精品国产91久久久久| 激情视频网站在线观看| 亚洲高清免费观看高清完整版在线观看| 九一在线免费观看| 久久精品人人做人人综合| 岛国精品一区二区三区| 国产一区三区三区| 中文字幕 91| 日韩福利视频网| 日本一区二区黄色| 99亚洲伊人久久精品影院红桃| 在线观看成人免费| 91精品国产乱码久久久久久久| 动漫精品一区二区| 国产成年人在线观看| 国产亚洲一区| 欧美精品久久久| 欧美调教视频| 精品视频第一区| youjizz亚洲| eeuss一区二区三区| 警花av一区二区三区| 91免费视频国产| 亚洲免费看片| 91精品久久久久久久久久另类| 国产精品迅雷| 日本国产精品视频| 北岛玲heyzo一区二区| 亚洲3p在线观看| 成人免费观看在线观看| 97精品视频在线播放| 91福利在线尤物| 91极品女神在线| 免费毛片b在线观看| 久久免费视频网| 国产美女精品写真福利视频| 97精品视频在线播放| 亚洲性色av| 最近2019中文字幕第三页视频| 亚洲xxxxxx| 精品国产一区二区三区久久| 国产黄色在线观看| 色与欲影视天天看综合网| gogo高清午夜人体在线| 97精品免费视频| 最新欧美电影| 国产视频福利一区| 91麻豆精品国产91久久久久推荐资源| 俄罗斯精品一区二区| 欧美sss在线视频| 欧美aaaaa喷水| 青青一区二区三区| 久久人妻无码一区二区| 欧美午夜一区| 欧美日韩二三区| 免费不卡在线视频| 人妻巨大乳一二三区| 不卡一区在线观看| av黄色在线免费观看| 1024亚洲合集| 日韩欧美亚洲一区二区三区| 欧美日韩在线视频一区二区| 羞羞色院91蜜桃| 日韩免费电影一区| 国产又爽又黄网站亚洲视频123| 亚洲网站在线看| xvideos国产在线视频| 国产综合在线看| 日本一区免费网站| 99三级在线| 精品国产视频| 国产一区 在线播放| 久久亚洲美女| 男人午夜视频在线观看| 91影院在线免费观看| 9.1片黄在线观看| 亚洲成人中文在线| 中文字幕在线观看精品| 精品三级在线看| 二区在线观看| 久久久噜噜噜久久久| 日韩网站中文字幕| 超碰97在线人人| 成人精品久久| 国产精品国产亚洲精品看不卡| 美腿丝袜亚洲色图| yy6080午夜| 亚洲男人的天堂av| 日韩黄色一级视频| 精品久久久久久久久久久久久久久| 都市激情一区| 国语自产精品视频在线看一大j8| 久久亚洲精品人成综合网| 国产一级二级三级精品| 久久精品亚洲欧美日韩精品中文字幕| 国产极品粉嫩福利姬萌白酱| 国产乱淫av一区二区三区| 亚洲一级中文字幕| 亚洲成人动漫在线观看| 国产乱码久久久久| 国产亚洲一区二区在线| 波多野一区二区| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美美女在线观看| 久久亚洲中文字幕无码| 国内成人免费视频| 免费看的黄色录像| 91福利区一区二区三区| 日夜干在线视频| 高清欧美电影在线| 中文无码日韩欧| 国内外成人激情免费视频| 国产精品视频| 最近日本中文字幕| 亚洲大片在线观看| 国产乱淫a∨片免费视频| 中文字幕精品视频| 成人在线免费| 日韩精品久久久免费观看| 久久久久久久欧美精品| av鲁丝一区鲁丝二区鲁丝三区| 一个色在线综合| 精品人妻无码一区二区三区蜜桃一| 日韩中文字幕久久| 福利一区二区免费视频| 神马影院午夜我不卡影院| 日韩影院免费视频| www..com.cn蕾丝视频在线观看免费版| 丁香五六月婷婷久久激情| 污污的视频网站在线观看| 午夜精品在线视频| 欧美丝袜美腿| 熟妇人妻va精品中文字幕| 国产三级精品视频| 中文字幕av网站| 久久人人爽人人爽人人片亚洲| 成人在线分类| 日本一本草久p| 国产盗摄精品一区二区三区在线| 久草综合在线视频| 欧美不卡视频一区| 欧美少妇网站| 日韩欧美国产二区| 极品少妇xxxx精品少妇偷拍 | 999成人网| 日韩av自拍偷拍| 亚洲制服丝袜一区| 天天摸天天碰天天爽天天弄| 日本精品视频在线播放| 狠狠操综合网| 女同激情久久av久久| 亚洲精选视频免费看| 好吊视频一二三区| 日产精品99久久久久久| 成人亚洲一区| 色哟哟网站在线观看| 精品国产乱码久久久久久虫虫漫画| 每日更新av在线播放| 国产精品久久久久高潮| 91精品国产调教在线观看| 图片区偷拍区小说区| 欧美丝袜第一区| 视频免费一区| 风间由美一区二区三区| 久久亚洲电影| 超碰手机在线观看| 日韩av最新在线观看| 91亚洲精品| 日韩a级黄色片| 久久久另类综合| 国产av精国产传媒| 日韩av手机在线| 欧美日韩免费| 国产伦精品一区二区三区视频女| 日韩欧美国产综合| 日韩欧美另类一区二区| 可以免费看的黄色网址| 久久久无码精品亚洲日韩按摩| 国产一区二区小视频| 午夜精品一区二区三区av| 天天做天天爱天天综合网| 免费成人深夜夜行p站| 欧美精品一卡两卡| 电影一区二区三| 国产xxxx振车|