基于C++17實現(xiàn)的手寫線程池
接著上文,我們使用了基于C++11實現(xiàn)的手寫線程池,自己實現(xiàn)了Any類,Semaphore類以及Result類的開發(fā),其中很多細(xì)節(jié)是值得學(xué)習(xí)的,隨著不斷更新,在C++17中提供future類,使得代碼更加輕量化。接下來我們來看一下源碼剖析:
threadpool.h
在最終版,除去 submitTask 以及 threadFunc 其余部分和基于C++11是一樣的,這里不自定義Any類,Semaphore類以及Result類,使用C++17中提供的future類,并且支持用戶輸入任意類型,任意參數(shù)數(shù)量的任務(wù).
首先,定義了一個Task任務(wù),不確定函數(shù)返回值類型
using Task = std::function<void()>;
submitTask
template<typename Func,typename... Args> //右值引用,引用折疊,可變參 auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))> { //打包任務(wù) 放入任務(wù)隊列里面 using RType = decltype(func(args...)); //返回值類型 auto task = std::make_shared<std::packaged_task<RType()>>( std::bind(std::forward<Func>(func), std::forward<Args>(args)...)); //forward保持左值或者右值特性 std::future<RType> result = task->get_future(); //把參數(shù)全部綁定到函數(shù)上 //獲取鎖 std::unique_lock<std::mutex> lock(taskQueMtx_); if (!notFull_.wait_for(lock, std::chrono::seconds(1), [&]()->bool {return taskQue_.size() < (size_t)taskQueMaxThresHold_; })) { std::cerr << "task queue is full,submit task fail." << std::endl; auto task = std::make_shared<std::packaged_task<RType()>>( []()->RType { return RType(); }); (*task)(); return task->get_future();; } taskQue_.emplace([task]() { (*task)(); }); taskSize_++; notEmpty_.notify_all(); if (poolMode_ == PoolMode::MODE_CACHED && taskSize_ > idleThreadSize_ && curThreadSize_ < threadSizeThresHold_) { std::cout << ">>> create new thread" << std::endl; //創(chuàng)建thread線程對象 auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1)); //threads_.emplace_back(std::move(ptr)); //資源轉(zhuǎn)移 int threadId = ptr->getId(); threads_.emplace(threadId, std::move(ptr)); threads_[threadId]->start(); //啟動線程 //修改線程個數(shù)相關(guān)的變量 curThreadSize_++; idleThreadSize_++; } //返回任務(wù)的Result對象 return result; }
- 為了讓submitTask可以接收任意任務(wù)函數(shù)和任意數(shù)量的參數(shù),我們使用可變參模板編程
template<typename Func,typename... Args>
; - submitTask返回的類型是由decltype推導(dǎo)出來的,func(args…) 參數(shù)傳入函數(shù),通過decltype可以推導(dǎo)出來類型,但是不會計算表達(dá)式;
- 打包任務(wù) 放入任務(wù)隊列里面,使用 decltype(func(args…)); 來確定返回值類型RType;
- 確定task,把參數(shù)全部綁定到函數(shù)上
- 把參數(shù)全部綁定到函數(shù)上;
- 通過future類的
get_future();
獲取返回值
接下來就執(zhí)行任務(wù),由于using Task = std::function<void()>;任務(wù)隊列的任務(wù)沒有返回值,但是上面的task有返回值,所以使用中間層匿名函數(shù)對象,當(dāng)調(diào)用匿名函數(shù)時,實際上就是調(diào)用(*task)();,task解引用以后就是packaged_task,然后()執(zhí)行了task任務(wù),在線程函數(shù)中,執(zhí)行task()相當(dāng)于就執(zhí)行了packaged_task對象。
啟動線程,執(zhí)行線程函數(shù)
void threadFunc(int threadid) { auto lastTime = std::chrono::high_resolution_clock().now(); for (;;) { Task task; { //獲取鎖 std::unique_lock<std::mutex> lock(taskQueMtx_); std::cout << "tid:" << std::this_thread::get_id()<< "嘗試獲取任務(wù)..." << std::endl; while (taskQue_.size() == 0) { if (!isPoolRuning_) { threads_.erase(threadid); std::cout << "threadid:" << std::this_thread::get_id() << "exit!" << std::endl; exitCond_.notify_all(); return; } if (poolMode_ == PoolMode::MODE_CACHED) { //超時返回std::cv_status::timeout if (std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1))) { auto now = std::chrono::high_resolution_clock().now(); auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime); if (dur.count() >= THREAD_MAX_IDLE_TIME&& curThreadSize_ > initThreadSize_) { threads_.erase(threadid); curThreadSize_--; idleThreadSize_--; std::cout << "threadid:" << std::this_thread::get_id()<< "exit!" << std::endl; return; } } } else { //等待notEmpty_條件 notEmpty_.wait(lock); } } idleThreadSize_--; std::cout << "tid:" << std::this_thread::get_id() << "獲取任務(wù)成功..." << std::endl; //從任務(wù)隊列中取一個任務(wù)出來 task = taskQue_.front(); taskQue_.pop(); taskSize_--; //若依然有剩余任務(wù),繼續(xù)通知其他線程執(zhí)行任務(wù) if (taskQue_.size() > 0) { notEmpty_.notify_all(); } notFull_.notify_all(); } if (task != nullptr) { task(); //執(zhí)行std::function<void()>; } idleThreadSize_++; auto lastTime = std::chrono::high_resolution_clock().now(); } }
除去執(zhí)行任務(wù)的地方改變了,其余沒變;
這里取出task,然后task(); 相當(dāng)于執(zhí)行了(*task)();也就是執(zhí)行了packaged_task對象。
測試文件
#include <iostream> #include <functional> #include <thread> #include <future> #include <chrono> #include "threadpool.h" using namespace std; int sum1(int a, int b) { std::this_thread::sleep_for(std::chrono::seconds(5)); return a + b; } int sum2(int a, int b,int c) { std::this_thread::sleep_for(std::chrono::seconds(5)); return a + b + c; } int main() { ThreadPool pool; //pool.setMode(PoolMode::MODE_CACHED); pool.start(2); future<int> res1 = pool.submitTask(sum1, 1, 2); future<int> res2 = pool.submitTask(sum2, 1, 2,3); future<int> res3 = pool.submitTask([](int d, int e)->int { int sum = 0; for (int i = d; i < e; i++) sum += i; return sum; },1, 100); future<int> res4 = pool.submitTask([](int d, int e)->int { int sum = 0; for (int i = d; i < e; i++) sum += i; return sum; }, 1, 100); future<int> res5 = pool.submitTask([](int d, int e)->int { int sum = 0; for (int i = d; i < e; i++) sum += i; return sum; }, 1, 100); cout << res1.get() << endl; cout << res2.get() << endl; cout << res3.get() << endl; cout << res4.get() << endl; cout << res5.get() << endl; }
ThreadPool定義對象,開啟線程,2個線程
這里可以提交任意類型的,任意參數(shù)個數(shù)的任務(wù)以及匿名函數(shù);
pool.setMode(PoolMode::MODE_CACHED); 下:
好了~ 基于C++17實現(xiàn)的線程池就剖析結(jié)束了,實際上使用了future類以及packaged_task(function函數(shù)對象),方便了我們項目的實現(xiàn),更多相關(guān)C++17 手寫線程池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mac OS上搭建Apache+PHP+MySQL開發(fā)環(huán)境的詳細(xì)教程
這篇文章主要介紹了Mac OS上搭建Apache+PHP+MySQL開發(fā)環(huán)境的詳細(xì)教程,包括常見的PHP連接MySQL失敗問題的解決辦法,需要的朋友可以參考下2016-01-01深入解讀C++ 內(nèi)聯(lián)函數(shù)inline|nullptr
內(nèi)聯(lián)函數(shù):用** inline 修飾的函數(shù)叫做內(nèi)聯(lián)函數(shù),編譯時C++編譯器會在調(diào)用的地方展開內(nèi)聯(lián)函數(shù)**,這樣調(diào)用內(nèi)聯(lián)函數(shù)就需要創(chuàng)建棧楨,就提高效率了,這篇文章給大家介紹C++ 內(nèi)聯(lián)函數(shù)inline|nullptr的相關(guān)知識,感興趣的朋友跟隨小編一起看看吧2024-07-07C語言詳細(xì)圖解浮點型數(shù)據(jù)的存儲實現(xiàn)
使用編程語言進(jìn)行編程時,需要用到各種變量來存儲各種信息。變量保留的是它所存儲的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個變量時,就會在內(nèi)存中保留一些空間。您可能需要存儲各種數(shù)據(jù)類型的信息,操作系統(tǒng)會根據(jù)變量的數(shù)據(jù)類型,來分配內(nèi)存和決定在保留內(nèi)存中存儲什么2022-05-05