天天看點

c++ 弱雞線程池的實作

線程池

把之前做的筆記一次性挪到部落格上好喽

這是一個固定數量的線程池,線程任務可帶參數,異步傳回傳回值,本菜雞花了一天才看明白這是怎麼回事,用到了很多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線程。