-
目录
-
线程池要素
- 任务队列类,线程池类。线程池类中又有3个要素管理线程,生产线程,消费线程。所以先来实现任务队列类。
-
任务队列类
- 此处使用STL容器的queue,它是先入先出的数据结构,取头部和尾部元素更方便,且容器是动态长度。程序的目标任务采用结构体实现,存放任务函数的地址和任务函数所需的参数
-
struct Task // 任务结构体 { Task() // 无参构造 { function = nullptr; arg = nullptr; } Task(void (*function)(void* arg), void* arg) // 有参构造 { this->function = function; this->arg = arg; } void (*function)(void* arg); void* arg; }; class TaskQueue { public: TaskQueue(); ~TaskQueue(); //添加任务 void addTask(Task target); void addTask(void (*function)(void* arg), void* arg); //取任务 Task getTask(); //获取队列中任务个数 //虽然可以直接采用m_taskQ.size()获取个数,但是这个任务简单,CPU资源消耗在了函数调用上,效率低 //在函数频繁调用且函数内部代码很少的情况下使用内联,提高程序效率 //若函数体内有循环,递归就不采用内联 //在类中实现函数的功能也是隐式内联,即使没写inline inline int getTaskNum() { return m_taskQ.size(); } private: pthread_mutex_t m_mutex; std::queue<Task> m_taskQ;//使用队列进行存储任务队列,对比起vector取头部数据更方便 };
//任务队列.cpp #include "TaskQueue.h" TaskQueue::TaskQueue() { pthread_mutex_init(&m_mutex,NULL);//初始化任务队列内的互斥锁 } TaskQueue::~TaskQueue() { pthread_mutex_destroy(&m_mutex); //释放互斥锁 } // 添加任务的重载 void TaskQueue::addTask(Task target) { pthread_mutex_lock(&m_mutex); m_taskQ.push(target); pthread_mutex_unlock(&m_mutex); } void TaskQueue::addTask(void (*function)(void* arg), void* arg) { pthread_mutex_lock(&m_mutex); m_taskQ.push(Task(function, arg)); pthread_mutex_unlock(&m_mutex); } Task TaskQueue::getTask() { if (m_taskQ.size() > 0) { pthread_mutex_lock(&m_mutex); Task tmp; tmp = m_taskQ.front(); // 取头部任务 m_taskQ.pop(); // 删除取出的任务 pthread_mutex_unlock(&m_mutex); return tmp; } }
线程池类
-
class ThreadPool { public: ThreadPool(int min, int max); // 初始化线程池 ~ThreadPool(); //销毁线程池 // 给线程池添加任务 重载为两种添加方式 void AddTask(void(*func)(void*), void* arg); void AddTask(Task target); // 获取线程池中工作的线程的个数 int getBusyNum(); // 获取线程池中活着的线程的个数 int getAliveNum(); private: // 任务队列 TaskQueue* taskQ; int minNum; // 最小线程数量 int maxNum; // 最大线程数量 int busyNum; // 忙的线程的个数 int liveNum; // 存活的线程的个数 int exitNum; // 要销毁的线程个数 bool shutdown; // 是不是要销毁线程池, 销毁为1, 不销毁为0 pthread_t managerID; // 管理者线程ID pthread_t* threadIDs; // 工作的线程ID pthread_mutex_t mutexPool; // 锁整个的线程池 锁越多效率越高但是也更容易出错 pthread_cond_t notEmpty; // 任务队列是不是空了 STL容器空间可以增长 static const int NUMBER = 2; // 工作的线程(消费者线程)任务函数 //以下三个函数并不需要暴露给用户调用 static void* worker(void* arg); // 管理者线程任务函数 static void* manager(void* arg); // 单个线程退出 void threadExit(); };
#include "ThreadPool.h" #include <iostream> #include <string.h> #include <string> #include <unistd.h> using namespace std; ThreadPool::ThreadPool(int min, int max) { //只需要实例化taskQ和工作线程threadIDs do { taskQ = new TaskQueue; if (taskQ == nullptr) { std::cout << "new taskQ fail...\n"; break; } threadIDs = new pthread_t[max]; if (threadIDs == nullptr) { std::cout<<"new threadIDs fail...\n"; break; } memset(threadIDs, 0, sizeof(pthread_t) * max); //把空间清零 minNum = min; maxNum = max; busyNum = 0; liveNum = min; // 和最小个数相等 exitNum = 0; if (pthread_mutex_init(&mutexPool, NULL) != 0 || pthread_cond_init(¬Empty, NULL) != 0) { std::cout<<"mutex or condition init fail...\n"; break; } shutdown = false; // 创建线程 pthread_create(&managerID, NULL, manager, this); // 此处需要的实际的函数地址,类的动态函数只有在类被实例化后才有地址 // 解决方法:1.声明为static静态函数 2把manager函数声明到类外面去 for (int i = 0; i < min; ++i) { pthread_create(&threadIDs[i], NULL, worker, this); } return; // 防止执行下面的释放操作 } while (0); // 释放资源 if (threadIDs) delete[] (threadIDs); if (taskQ) delete (taskQ); } ThreadPool::~ThreadPool() { // 关闭线程池 shutdown = true; // 阻塞回收管理者线程 pthread_join(managerID, NULL); // 唤醒阻塞的消费者线程 for (int i = 0; i < liveNum; ++i) { pthread_cond_signal(¬Empty); } // 释放堆内存 if (taskQ) { delete taskQ; } if (threadIDs) { delete[] threadIDs; } pthread_mutex_destroy(&mutexPool); pthread_cond_destroy(¬Empty); } void ThreadPool::AddTask(void(*func)(void*), void* arg) { if (shutdown) { pthread_mutex_unlock(&mutexPool); return; } // 添加任务 taskQ->addTask(func, arg); //唤醒消费者线程 pthread_cond_signal(¬Empty); } void ThreadPool::AddTask(Task target) { if (shutdown) { pthread_mutex_unlock(&mutexPool); return; } // 添加任务 taskQ->addTask(target); //唤醒消费者线程 pthread_cond_signal(¬Empty); } int ThreadPool::getBusyNum() { pthread_mutex_lock(&mutexPool); int busyNum = this->busyNum; pthread_mutex_unlock(&mutexPool); return busyNum; } int ThreadPool::getAliveNum() { pthread_mutex_lock(&mutexPool); int aliveNum = liveNum; pthread_mutex_unlock(&mutexPool); return aliveNum; } void* ThreadPool::worker(void* arg) { ThreadPool* pool = (ThreadPool*)arg; //ThreadPool* pool = static_cast<ThreadPool*> (arg); C++强制转换 while (1) { pthread_mutex_lock(&pool->mutexPool);// 静态方法无法调用动态成员 故要实例化的pool指针去操作互斥锁 // 当前任务队列是否为空 while (pool->taskQ->getTaskNum() == 0 && !pool->shutdown) { // 阻塞工作线程 pthread_cond_wait(&pool->notEmpty, &pool->mutexPool); // 判断是不是要销毁线程 if (pool->exitNum > 0) { pool->exitNum--; if (pool->liveNum > pool->minNum) { pool->liveNum--; pthread_mutex_unlock(&pool->mutexPool); pool->threadExit(); } } } // 判断线程池是否被关闭了 if (pool->shutdown) { pthread_mutex_unlock(&pool->mutexPool); pool->threadExit(); } // 从任务队列中取出一个任务 Task task = pool->taskQ->getTask(); pool->busyNum++; // 解锁 pthread_mutex_unlock(&pool->mutexPool); cout<<"thread "<<to_string(pthread_self()) <<"start working...\n"; task.function(task.arg); //执行任务 delete task.arg; task.arg = nullptr; cout<<"thread %ld end working...\n"<< pthread_self(); pthread_mutex_lock(&pool->mutexPool); pool->busyNum--; pthread_mutex_unlock(&pool->mutexPool); } return nullptr; } void* ThreadPool::manager(void* arg) { ThreadPool* pool = (ThreadPool*)arg; while (!pool->shutdown) { // 每隔3s检测一次 sleep(3); // 取出线程池中当前线程的数量 // 取出忙的线程的数量 pthread_mutex_lock(&pool->mutexPool); int queueSize = pool->taskQ->getTaskNum(); int liveNum = pool->liveNum; int busyNum = pool->busyNum; pthread_mutex_unlock(&pool->mutexPool); // 添加线程 // 任务的个数>存活的线程个数 && 存活的线程数<最大线程数 if (queueSize > liveNum && liveNum < pool->maxNum) { pthread_mutex_lock(&pool->mutexPool); int counter = 0; for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum; ++i) { if (pool->threadIDs[i] == 0) { pthread_create(&pool->threadIDs[i], NULL, worker, pool); counter++; pool->liveNum++; } } pthread_mutex_unlock(&pool->mutexPool); } // 销毁线程 // 忙的线程*2 < 存活的线程数 && 存活的线程>最小线程数 if (busyNum * 2 < liveNum && liveNum > pool->minNum) { pthread_mutex_lock(&pool->mutexPool); pool->exitNum = NUMBER; pthread_mutex_unlock(&pool->mutexPool); // 让工作的线程自杀 for (int i = 0; i < NUMBER; ++i) { pthread_cond_signal(&pool->notEmpty); } } } return NULL; } void ThreadPool::threadExit() { pthread_t tid = pthread_self(); for (int i = 0; i < maxNum; ++i) { if (threadIDs[i] == tid) { threadIDs[i] = 0; cout << "threadExit() called, "<<tid<<" exiting...\n"; break; } } pthread_exit(NULL); }
测试代码
-
#include "ThreadPool.h" #include <unistd.h> #include <iostream> void taskFunc(void* arg) { int num = *(int*)arg; printf("thread %ld is working, number = %d\n", pthread_self(), num); sleep(1); } int main() { // 创建线程池 ThreadPool pool(3, 10); for (int i = 0; i < 100; ++i) { int* num = new int; *num = i + 100; pool.AddTask(taskFunc, num); } sleep(30); // 等待子线程工作结束 return 0; }
运行结果
-
-
学会了如何构建线程池且利用线程池去进行线程同步解决并发编程问题。
C++11特性下的线程池构建
最新推荐文章于 2023-12-30 15:36:58 发布