一、什麼是線程池
在介紹如何實現一個高效的線程池之前,我們首先需要了解線程池的概念。
線程池是一種多線程處理方式,旨在避免在應用程序中重複創建和銷毀線程帶來的開銷,通過事先創建一定數量的線程存放在一個線程池中,當有任務需要處理時,就從線程池中取一個線程來執行。當任務完成後,該線程不銷毀,而是返回線程池中,等待接下來的任務處理。
線程池可以提高多線程編程的效率,減少線程的創建次數,避免線程數目過多的情況下出現的各種問題。接下來,我們將通過代碼實現來進一步了解線程池的運作原理。
二、如何實現一個線程池
下面是一個簡單的線程池實現的代碼示例:
#include
#include
#include
#include
#include
class ThreadPool {
public:
ThreadPool(size_t num_threads) {
for (size_t i = 0; i < num_threads; ++i) {
threads_.emplace_back([this] {
for (;;) {
std::function task;
{
std::unique_lock lock(mutex_);
condition_.wait(lock, [this] { return stop_ || !tasks_.empty(); });
if (stop_ && tasks_.empty())
return;
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
});
}
}
template
auto enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of::type> {
using return_type = typename std::result_of::type;
auto task = std::make_shared<std::packaged_task>
(std::bind(std::forward(f), std::forward(args)...));
std::future res = task->get_future();
{
std::unique_lock lock(mutex_);
if (stop_)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks_.emplace([task]() { (*task)(); });
}
condition_.notify_one();
return res;
}
~ThreadPool() {
{
std::unique_lock lock(mutex_);
stop_ = true;
}
condition_.notify_all();
for (std::thread &worker : threads_)
worker.join();
}
private:
std::vector threads_;
std::queue<std::function> tasks_;
std::mutex mutex_;
std::condition_variable condition_;
bool stop_ = false;
};
這個線程池實現包含了一個ThreadPool類和三個對外的介面函數:
- ThreadPool(size_t num_threads),一個構造函數,用於創建指定數量的線程。
- enqueue(F&& f, Args&&… args),用於向線程池中添加需要處理的任務。
- ~ThreadPool(),線程池的析構函數,用於銷毀全部的線程。
三、線程池的使用
使用這個線程池很簡單。我們只需要先創建一個ThreadPool對象並指定線程數量,然後通過enqueue函數向線程池添加需要處理的任務。下面是一個示例代碼:
ThreadPool pool(4);
auto result = pool.enqueue([] (int x) {
return x * x;
}, 42);
std::cout << result.get() << std::endl;
這段代碼首先創建一個ThreadPool對象pool,並指定線程數量為4。接著通過enqueue函數向線程池添加任務:一個lambda函數,用於計算給定的整數的平方。最後將任務的返回結果列印出來。
四、線程池的優化
上述的線程池實現非常基礎,但它仍然有些可以優化的地方。例如,它只有一個任務隊列和一個條件變數,這裡有些風險,如果enqueue函數中有異常拋出,輕則導致隊列中存在錯誤的任務,重則導致整個線程池崩潰。
因此,我們可以為任務隊列和條件變數各自設置一個鎖,使得enqueue函數成為線程安全的函數,保證任務進入隊列的正確性。請看下面的代碼示例:
class ThreadPool {
public:
ThreadPool(size_t num_threads) : stop_(false) {
for (size_t i = 0; i < num_threads; ++i) {
threads_.emplace_back([this] {
for (;;) {
std::function task(nullptr);
{
std::unique_lock lock(mutex_);
condition_.wait(lock, [this] {
return stop_ || !tasks_.empty();
});
if (stop_ && tasks_.empty())
return;
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
});
}
}
template
auto enqueue(F&& f, Args&&... args)
->std::future<typename std::result_of::type> {
using return_type = typename std::result_of::type;
auto task = std::make_shared<
std::packaged_task>(
std::bind(std::forward(f), std::forward(args)...));
std::future res = task->get_future();
{
std::unique_lock lock(mutex_);
if (stop_)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks_.emplace([task]() { (*task)(); });
}
condition_.notify_one();
return res;
}
~ThreadPool() {
{
std::unique_lock lock(mutex_);
stop_ = true;
}
condition_.notify_all();
for (std::thread& worker : threads_)
worker.join();
}
private:
std::vector threads_;
std::queue<std::function> tasks_;
std::mutex task_mutex_;
std::mutex stop_mutex_;
std::mutex mutex_;
std::condition_variable condition_;
bool stop_;
};
五、總結
本文從線程池的概念和原理入手,介紹了如何使用C++語言實現一個基本的線程池。同時,本文還提到了線程池的優化點,為實際生產環境中的應用提供了一些需要注意的技巧。
線程池是多線程編程中一種比較常見的工具,它可以提高程序的並發處理能力,避免線程頻繁的創建和銷毀對系統帶來的負擔。希望本文能夠幫助大家理解線程池的意義以及實現方法,對提高應用程序性能有所幫助。
原創文章,作者:BXWM,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/131897.html
微信掃一掃
支付寶掃一掃