歡迎您光臨本站 註冊首頁

C++實現線程池的簡單方法示例

←手機掃碼閱讀     e36605 @ 2020-05-06 , reply:0

最近自己寫了一個線程池。
總的來說,線程池就是有一個任務隊列,一個線程隊列,線程隊列不斷地去取任務隊列中的任務來執行,當任務隊列中為空時,線程阻塞等待新的任務添加過來。
我是用queue來存放任務,vector存放thread*,然後用condition_variable 來設置線程阻塞和喚醒。
下面直接上代碼吧。
線程池類頭文件Thread_Pool.h
/******************************************** 

線程池頭文件 Author:十面埋伏但莫慌 Time:2020/05/03 *********************************************

#pragma once 

#ifndef _THREAD_POOL_H_ 

#define _THREAD_POOL_H_ 

#include

#include#include#include#include#includetypedef std::functionFunc;//定義線程執行函數類型,方便後面編碼使用。 //任務類 class Task { public: Task() {} ~Task() {} int push(Func func);//添加任務; int getTaskNum();//獲得當前隊列中的任務數; Func pop();//取出待執行的任務; public: std::mutex mx;//鎖; private: std::queuetasks;//任務隊列 }; //線程池類 class Thread_Pool { public: Thread_Pool() :IsStart(false) {} ~Thread_Pool(); int addTasks(Func tasks);//添加任務; void start();//開啟線程池; void stop();//關閉線程池; void run();//線程工作函數; int getTaskNum();//獲得當前隊列中的任務數; private: static const int maxThreadNum = 3;//最大線程數為3; std::condition_variable cond;//條件量; std::vectorthreads;//線程向量; std::atomicIsStart;//原子變量,判斷線程池是否運行; Task tasks;//任務變量; }; #endif
然後是線程池類成員函數定義文件Thread_Pool.cpp

/******************************************** 

線程池CPP文件 Author:十面埋伏但莫慌 Time:2020/05/03 *********************************************

/ #include "Thread_Pool.h" 

#includeint Task::push(Func func) { std::unique_locklock(mx); try { tasks.emplace(func); } catch (std::exception e) { throw e; return -1; } return 0; } int Task::getTaskNum() { return tasks.size(); } Func Task::pop() { std::unique_locklock(mx); Func temp; if (tasks.empty()) return temp; else { temp = tasks.front(); tasks.pop(); return temp; } } int Thread_Pool::addTasks(Func func) { int ret = tasks.push(func); cond.notify_one(); return ret; } void Thread_Pool::start() { if (!IsStart) { IsStart = true; for (int i = 0; i < maxThreadNum; i++) { threads.emplace_back(new std::thread(std::bind(&Thread_Pool::run,this))); } } } 

void Thread_Pool::run() { while (IsStart) { Func f; if (tasks.getTaskNum() == 0 && IsStart) { std::unique_locklock(tasks.mx); cond.wait(lock); } if (tasks.getTaskNum() != 0 && IsStart) { f = tasks.pop(); if(f) f(); } } } int Thread_Pool::getTaskNum() { return tasks.getTaskNum(); } void Thread_Pool::stop() { IsStart = false; cond.notify_all(); for (auto T : threads) { std::cout << "線程 " << T->get_id() << " 已停止。" << std::endl; T->join(); if (T != nullptr) { delete T; T = nullptr; } } std::cout << "所有線程已停止。" << std::endl; } Thread_Pool::~Thread_Pool() { if (IsStart) { stop(); } }
最後是測試用的main.cpp

最後是測試用的main.cpp

#include

#include "Thread_Pool.h" using namespace std; 

void string_out_one() { cout << "One!" << endl; } 

void string_out_two() { cout << "Two!" << endl; }

 void string_out_three() { cout << "Three!" << endl; } 

int main() { { Thread_Pool Pool; try { Pool.start(); } catch (std::exception e) { throw e; cout << "線程池創建失敗。" << endl; } for (int i = 0; i < 50000 ;) { if (Pool.getTaskNum() < 1000) { Pool.addTasks(string_out_one); Pool.addTasks(string_out_two); Pool.addTasks(string_out_three); std::cout << i++ << std::endl; } } getchar(); } getchar(); return 0; }
執行的效果如下:
線程喚醒和阻塞的邏輯就是在線程工作函數run函數中,判斷隊列是否為空,若為空則設置鎖並調用condition變量的wait函數,釋放這個線程中的鎖並阻塞線程,等待任務隊列中新的任務添加進來後,
condition變量通過notify_one()隨機喚醒一個在wait的線程,取出隊列中的任務執行。
寫這個線程池的過程中碰到的最主要需要注意的就是鎖的使用,在對隊列的寫和釋放時要注意加鎖,在需要阻塞線程時,要注意通過{}設置鎖的範圍。
IsStart是原子的,所以在寫這個變量的時候沒有另外加鎖。
目前我覺得這個線程池的缺陷就是可執行函數的類型被寫死了,有嘗試對Task類使用模板類,但是在Thread_Pool中還是要指明Task模板類的類型參數,要是有大神指點下就好了- -。
就先記錄這麼多,感覺這個線程池的還是有很多可以改進的地方的,也歡迎大家指出不足。

執行的效果如下:
線程喚醒和阻塞的邏輯就是在線程工作函數run函數中,判斷隊列是否為空,若為空則設置鎖並調用condition變量的wait函數,釋放這個線程中的鎖並阻塞線程,等待任務隊列中新的任務添加進來後,
condition變量通過notify_one()隨機喚醒一個在wait的線程,取出隊列中的任務執行。
寫這個線程池的過程中碰到的最主要需要注意的就是鎖的使用,在對隊列的寫和釋放時要注意加鎖,在需要阻塞線程時,要注意通過{}設置鎖的範圍。
IsStart是原子的,所以在寫這個變量的時候沒有另外加鎖。
目前我覺得這個線程池的缺陷就是可執行函數的類型被寫死了,有嘗試對Task類使用模板類,但是在Thread_Pool中還是要指明Task模板類的類型參數,要是有大神指點下就好了- -。
就先記錄這麼多,感覺這個線程池的還是有很多可以改進的地方的,也歡迎大家指出不足。

[e36605 ] C++實現線程池的簡單方法示例已經有342次圍觀

http://coctec.com/docs/c/language/show-post-233123.html