C++ 学习笔记实战写一个简单的线程池示例
作者:Totn
这篇文章主要为大家介绍了C++实现一个简单的线程池学习实战,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
线程池的工作原理
- 在构造函数中,创建指定数量的工作线程,并将它们存储在
workers
向量中。 - 每个工作线程都通过无限循环来执行任务队列中的任务。
- 设置一个
public
函数,接收新任务,封装为std::function<void()>
并添加到任务队列中。 - 等待的工作线程会被唤醒并获取任务执行,直到线程池被销毁或调用
stop()
函数停止。
第一步, 创建指定数量的工作线程
class ThreadPool { public: ThreadPool(int num){ // 启动指定数量的线程 for(int i = 0; i < num; i++ ) { // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的 // 添加一个lambda函数即为创建一个线程 workers.emplace_back([this]{ // code... }); } } private: // 用向量储存线程 std::vector<std::thread> workers; }
第二步在每个线程中启动死循环,依次从任务队列中取中任务,直到队列为空或设置stop
标识为true;
第二步,在第一步基础上,添加代码
class ThreadPool { public: ThreadPool(int num): stoped(false) { // 启动指定数量的线程 for(int i = 0; i < num; i++ ) { // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的 // 添加一个lambda函数即为创建一个线程 workers.emplace_back([this]{ // 死循环 while(true) { // 设置一个task接收从队列中取出的任务 std::function<void()> task; { // 从队列中取任务要加锁,以避免重复读 std::unique_lock<std::mutex> lock(ququeMutex); // 如果stoped==true,或任务不为空,则使当前线程等待 condition.wait(lock, [this]{ return stroped || !tasks.empty(); }); // 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空) if (stoped && tasks.empty()) { return; } // 从队列中接收任务 task = std::move(tasks.front()); // 弹出已经接收的任务 tasks.pop(); } // 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁 } }); } } // 设置停止标识 void stop() { std::unique_lock<std::mutex> lock(queueMutex); stoped = true; } private: // 用向量储存线程 std::vector<std::thread> workers; // 工作队列 std::queue<std::function<void()> tasks; // 队列互斥锁 std::mutex queueMutex; // 条件变量 std::condition_variable condition; // stop标识 bool stoped; }
第三步,设置一个函数用从外部接收任务
// class ThreadBool... template<class F> void enqueue(F&& f){ { // 在域内启用锁, 保证加任务时没冲突 std::unique_lock<std::mutex> lock(queueMutex); // 转发任务到tasks队列中 tasks.emplace(std::forword<F>(f)); } // 通知一个线程起来接活 condition.notify_one(); } // ...
第四步,设置析构函数等待所有线程执行完成
// class ThreadBool ~ThreadPool(){ { std::unique_lock<std::mutex> lock(queueMutex); stoded = true; } // 阻塞等待所有线程 for(std::thread& worker: workers){ worker.join(); } }
最后完整代码
#include <vector> #include <thread> #include <queue> #include <mutex> #include <condition_variable> #include <functional> #include <iostream> thread_local int thr_num; class Task{ public run(int num = 0) { thr_num = num; std::cout << "当前任务号是" << thr_num << std::endl; } } class ThreadPool { public: ThreadPool(int num): stoped(false) { // 启动指定数量的线程 for(int i = 0; i < num; i++ ) { // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的 // 添加一个lambda函数即为创建一个线程 workers.emplace_back([this]{ // 死循环 while(true) { // 设置一个task接收从队列中取出的任务 std::function<void()> task; { // 从队列中取任务要加锁,以避免重复读 std::unique_lock<std::mutex> lock(ququeMutex); // 如果stoped==true,或任务不为空,则使当前线程等待 condition.wait(lock, [this]{ return stroped || !tasks.empty(); }); // 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空) if (stoped && tasks.empty()) { return; } // 从队列中接收任务 task = std::move(tasks.front()); // 弹出已经接收的任务 tasks.pop(); } // 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁 } }); } } // 设置停止标识 void stop() { std::unique_lock(std::mutex ) stoped = true; } private: // 用向量储存线程 std::vector<std::thread> workers; // 工作队列 std::queue<std::function<void()> tasks; // 队列互斥锁 std::mutex queueMutex; // 条件变量 std::condition_variable condition; // stop标识 bool stoped; } int main() { ThreadPool tp(4); Task task; for(int i = 0; i<4; i++) { int num = i; tp.enqueue([num, &task] { task.run(num); }); } return 0; }
以上就是C++ 学习笔记实战写一个简单的线程池示例的详细内容,更多关于C++ 线程池的资料请关注脚本之家其它相关文章!