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