線程池
把之前做的筆記一次性挪到部落格上好喽
這是一個固定數量的線程池,線程任務可帶參數,異步傳回傳回值,本菜雞花了一天才看明白這是怎麼回事,用到了很多c++11的特性,比如bind function future等
class ThreadPool{
public:
using Function = std::function<void()>; /* 成員函數 push-task 任務隊列推進去一個任務 構造函數 */
ThreadPool(int i);
~ThreadPool();
template<class F,class ...Args>
auto push_task(F &&f,Args &&...args)->
std::future<typename std::result_of<F(Args...)>::type>;
private:
/*私有成員 任務隊列一個 一個鎖 一個條件變量 一個bool 一個vector儲存線程 */ std::queue<Function,std::list<Function>> Tasks;
std::vector<std::thread> Threads;
std::mutex locks;
std::condition_variable variable;
bool end;
};
template<class F ,class ...Args>
auto zm::ThreadPool::push_task(F&&f,Args&&...args)
->std::future<typename std::result_of<F(Args...)>::type>{ /*把任務打包成一個可調用的智能指針*/ auto x = std::make_shared<std::packaged_task<typename std::result_of<F(Args...)>::type()>> (std::bind(std::forward<F>(f),std::forward<Args>(args)...));
std::future<typename std::result_of<F(Args...)>::type> res = x->get_future(); /*打包完畢*/
{ std::unique_lock<std::mutex> lock(locks);
Tasks.push([x]{(*x)();});
}
variable.notify_one();
return res;}
zm::ThreadPool::ThreadPool(int i):end(false){ if(i<1) i=1;
for(int j=0;j<i;++j){ Threads.emplace_back([this](){
while(1){
Function Task;
{ std::unique_lock<std::mutex> lock(locks); variable.wait(lock,[this]{return !Tasks.empty() || end;});
if(Tasks.empty()&&end == true) return ; Task = Tasks.front(); Tasks.pop(); }
(Task)();//執行這個函數
} }); }}zm::ThreadPool::~ThreadPool(){
{ std::unique_lock<std::mutex> lock(locks); end = true; } variable.notify_all(); for(int i=0;i<Threads.size();++i) Threads[i].join();}
簡述一下線程池的實作: 線程池類的資料結構包括: 一個裝有線程類的動态vector
一個互斥鎖用于線程通路共同資源
一個條件變量用于推送新任務後通知所有線程
一個底層為連結清單隊列用于裝載推送的任務 (推入類型為空的可調用對象)
一個bool變量 用于關閉所有的線程 用于析構
線程池類的操作分為三個:
構造函數
push——task
析構函數
構造函數操作包括:
根據構造輸入的參數确定線程池的數量,調用循環,在容器内調用emplace——back傳入
一個可調用對象,原地構造一個線程類。
可調用對象為一個死循環 :在循環内先上鎖 調用條件變量wait觀察隊列是否為空以及
是否标記了結束,采用wait第二個參數有兩個好處,第一個是防止虛假喚醒,第二個是 如果隊列不為空那麼就不在這裡等待了,直接取任務執行。任務被喚醒以後,檢查一下是由于結束被喚醒 還是由于有任務被喚醒,假如有任務被喚醒, 那麼就從隊列裡取出來,然後執行,假如是因為結束被喚醒,那麼就執行return退出線程。
push_back函數操作包括:輸入一個可調用對象名,以及一系列的參數,傳回一個future 去獲得這個可調用對象的結果 首先調用Bind函數通過完美轉發forward将可調用對象,以及可調用對象傳入的參數綁定在一起 将綁定後的可調用對象是沒有形參的,再package_TASK包裝一下,用于調用get_future得到 他的傳回類型麼,包裝過程中需要得到可調用對象的傳回類型的,于是需要result_of模闆去提取。
包裝完成後,為這個可調用對象配置設定一篇空間,拿一個智能指針指向這個可調用對象。
然後對隊列上鎖 推入一個可調用對象
這個可調用對象捕獲了這個pack打包了的指針,然後在邏輯裡面去執行他,是以這個可調用對象仍然是void()類型。
喚醒一個阻塞在條件變量上的線程
析構函數包括: 将一個類内成員end置為 1 并喚醒所有阻塞在條件變量上的線程 然後逐一join線程。