线程池的设计
在设计一个线程池之前,我们首先需要了解线程池大概有哪些部分组成:
1- 首先肯定是要有一定数量的worker线程,这些线程负责执行任务。他们需要从一个任务队列中取取出一个任务,然后执行这个任务。
2- 其次是任务队列,用于存放待执行的任务,worker也可以从这里面取任务。
3- 合理的同步机制,发布任务的时候和执行任务的时候需要一定的同步机制。
以上大概就是一个简单的线程池的组成部分了,具体的示意如下:
具体实现如下,详细解释见注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| #include <iostream> #include <thread> #include <mutex> #include <vector> #include <condition_variable> #include <queue> #include <string> #include <chrono> #include <functional> class ThreadPool { public: ThreadPool(int numThreads):stop(false){ for(int i=0;i<numThreads;i++) { threads.emplace_back([this](){ while(true) { std::unique_lock<std::mutex> lock(mtx); condition.wait(lock,[this](){ return !tasks.empty() || stop; }); if(stop && tasks.empty()) { return; } auto task(std::move(tasks.front())); tasks.pop(); mtx.unlock(); task(); } }); } } ~ThreadPool() { { std::unique_lock<std::mutex> lock(mtx); stop = true; } condition.notify_all(); for(auto& t : threads) { t.join(); } } template<typename F, typename... Args> void enqueue(F&& f, Args&&... args) { std::function<void()> task = \ std::bind(std::forward<F>(f),std::forward<Args>(args)...); { std::unique_lock<std::mutex>lock(mtx); tasks.emplace(std::move(task)); } condition.notify_one(); } private: std::vector<std::thread> threads; std::queue<std::function<void()>> tasks; std::mutex mtx; std::condition_variable condition; bool stop; };
void lockPrint(const std::string& info) { static std::mutex printMtx; std::lock_guard<std::mutex> lk(printMtx); std::cout<<info<<std::endl; } int main() { ThreadPool pool(4); for(int i=0;i<10;i++) { pool.enqueue([i]{ lockPrint("task "+std::to_string(i)+" running"); std::this_thread::sleep_for(std::chrono::seconds(2)); lockPrint("task "+std::to_string(i)+" done"); }); } }
|