生活随笔
收集整理的這篇文章主要介紹了
C++实现一个线程池
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、為什么使用線程池
大家都知道C++支持多線程開發,也就是支持多個任務并行運行,我們也知道線程的生命周期中包括創建、就緒、運行、阻塞、銷毀等階段,所以如果要執行的任務很多,每個任務都需要一個線程的話,那么頻繁的創建、銷毀線程會比較耗性能。
有了線程池就不用創建更多的線程來完成任務,它可以:
①降低資源的消耗,通過重復利用已經創建的線程降低線程創建和銷毀造成的消耗。
②提高相應速度,當任務到達的時候,任務可以不需要等到線程創建就能立刻執行。
③提高線程的可管理性,線程是稀缺資源,使用線程池可以統一的分配、調優和監控。
二、線程池的原理
通俗的講,線程池就是一個線程集合,里面已經提前創建好了若干個線程,當需要線程的時候到線程集合里獲取一個即可,這樣省去了創建線程的時間,當然也省去了系統回收線程的時間,當線程池里的線程都被使用了后,只能阻塞等待了,等待獲取線程池后被釋放的線程。
當線程池提交一個任務到線程池后,執行流程如下:
線程池先判斷核心線程池里面的線程是否都在執行任務。如果不是都在執行任務,則創建一個新的工作線程來執行任務。如果核心線程池中的線程都在執行任務,則判斷工作隊列是否已滿。如果工作隊列沒有滿,則將新提交的任務存儲到這個工作隊列中,如果工作隊列滿了,線程池則判斷線程池的線程是否都處于工作狀態。如果沒有,則創建一個新的工作線程來執行任務。如果已經滿了,則交給飽和策略來處理 ,也就是拒接策略。
一句話:管理一個任務隊列,一個線程隊列,然后每次去一個任務分配給一個線程去做,循環往復。
三、代碼實現
有什么問題?線程池一般是要復用線程,所以如果是取一個task分配給某一個thread,執行完之后再重新分配,在語言層面這是基本不能實現的:C++的thread都是執行一個固定的task函數,執行完之后線程也就結束了。所以該如何實現task和thread的分配呢?
讓每一個thread創建后,就去執行調度函數:循環獲取task,然后執行。
這個循環該什么時候停止呢?
很簡單,當線程池停止使用時,循環停止。
這樣一來,就保證了thread函數的唯一性,而且復用線程執行task。
總結一下,我們的線程池的主要組成部分有二:
- 任務隊列(Task Queue)
- 線程池(Thread Pool)
#ifndef THREAD_POOL_H
#define THREAD_POOL_H#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>class ThreadPool {public:ThreadPool(size_t); //構造函數template<class F, class... Args> //類模板auto enqueue(F&& f, Args&&... args)->std::future<decltype(func(args...))>;//任務入隊~ThreadPool(); //析構函數private:std::vector< std::thread > workers; //線程隊列,每個元素為一個Thread對象std::queue< std::function<void()> > tasks; //任務隊列,每個元素為一個函數對象 std::mutex queue_mutex; //互斥量std::condition_variable condition; //條件變量bool stop; //停止
};// 構造函數,把線程插入線程隊列,插入時調用embrace_back(),用匿名函數lambda初始化Thread對象
inline ThreadPool::ThreadPool(size_t threads) : stop(false){for(size_t i = 0; i<threads; ++i)workers.emplace_back([this]{for(;;){// task是一個函數類型,從任務隊列接收任務std::function<void()> task; {//給互斥量加鎖,鎖對象生命周期結束后自動解鎖std::unique_lock<std::mutex> lock(this->queue_mutex);//(1)當匿名函數返回false時才阻塞線程,阻塞時自動釋放鎖。//(2)當匿名函數返回true且受到通知時解阻塞,然后加鎖。this->condition.wait(lock,[this]{ return this->stop || !this->tasks.empty(); });if(this->stop && this->tasks.empty())return;//從任務隊列取出一個任務task = std::move(this->tasks.front());this->tasks.pop();} // 自動解鎖task(); // 執行這個任務}});
}// 添加新的任務到任務隊列
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)->std::future<decltype(func(args...))>
{// 獲取函數返回值類型 using return_type = decltype(func(args...));// 創建一個指向任務的智能指針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(queue_mutex); //加鎖if(stop)throw std::runtime_error("enqueue on stopped ThreadPool");tasks.emplace([task](){ (*task)(); }); //把任務加入隊列} //自動解鎖condition.notify_one(); //通知條件變量,喚醒一個線程return res;
}// 析構函數,刪除所有線程
inline ThreadPool::~ThreadPool()
{{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for(std::thread &worker: workers)worker.join();
}#endif
使用
#include <iostream>
#include <chrono>
#include "ThreadPool.h"void func()
{std::this_thread::sleep_for(std::chrono::milliseconds(100));std::cout<<"worker thread ID:"<<std::this_thread::get_id()<<std::endl;
}int main()
{ThreadPool pool(4);while(1){pool.enqueue(func);}
}
打印
?
參考:
基于C++11實現線程池 - 知乎
C++實現線程池_蓬萊道人的博客-CSDN博客_c++ 線程池
C++實現線程池_曉楓寒葉的博客-CSDN博客_c++ 線程池
C++17future類+可變參模板實現線程池_剛入門的代碼spa技師的博客-CSDN博客_c++17 可變參
總結
以上是生活随笔為你收集整理的C++实现一个线程池的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。