线程池编程简介:
在 我们的服务端的程序中运用了大量关于池的概念,线程池、连接池、内存池、对象池等等。使用池的概念后可以高效利用服务器端的资源,比如没有大量的线程在系 统中进行上下文的切换,一个数据库连接池,也只需要维护一定里的连接,而不是占用很多数据库连接资源。同时它们也避免了一些耗时的操作,比如创建一个线 程,申请一个数据库连接,而且可能就只使用那么一次,然后就立刻释放刚申请的资源,效率很低。
在我的上一篇blog中已经实现一个线程基类了,在这里我们只需要实现一个线程池类ThreadPool和该线程池调度的工作线程类WorkThread即可,而且WorkThread是继承自Thread类的。
实现思路:
一个简单的线程池的实现思路一般如下:
- 在ThreadPool中创建多个线程(WorkThreadk对象),每个线程均处于阻塞状态,等待任务的到来
- ThreadPool提供一个提交任务的接口,如post_job(ProcCallback func, void* data); post_job后会立即返回,不会阻塞
- ThreadPool维护一个空闲线程队列,当客户程序调用post_job()后,如果空闲队列中有空闲线程,则取出一个线程句柄,并设置任务再给出新任务通知事件即可,处理等待的线程捕捉到事件信号后便开始执行任务,执行完后将该线程句柄重新push到空闲线程队列中
- 该线程池采用回调函数方式
首先我们实现一个WorkThread类:
#include <pthread.h> #include <vector> #include <iostream> using namespace std; typedef void(*ProcCallBack)(void *); class WorkThread; class ThreadPool { friend class WorkThread; public: ThreadPool(){} ~ThreadPool(); int start_thread_pool(size_t thread_num = 5); //启动thread_num个线程 int stop_thread_pool(); //线束线程池 void destroy(); //销毁线程池所申请的资源 void post_job(ProcCallBack func, void* data); //提交任务接口,传入回调函数地址和参数 protected: WorkThread* get_idle_thread(); //从获得空闲队列中取得一个线程句柄 void append_idle_thread(WorkThread* pthread); //加入到thread_vec_和idl_que_中 void move_to_idle_que(WorkThread* idlethread); //将线程句柄加入到idle_que_中 private: size_t thr_num_; //线程数目 vector<WorkThread*> thr_vec_; //线程句柄集合 vector<WorkThread*> idle_que_; //空闲线程队列 private: // not implement ThreadPool(const ThreadPool&); ThreadPool& operator=(const ThreadPool&); }; class WorkThread { friend class ThreadPool; public: WorkThread(ThreadPool* pthr_pool) { hr_pool_ = pthr_pool; cb_func_ = NULL; param_ = NULL; } ~WorkThread(){} void set_job(ProcCallBack func, void *param) { cb_func_ = func; param_ = param; //notify(); } void run() { if (cb_func_) { cb_func_(param_); } cb_func_ = NULL; param_ = NULL; hr_pool_->move_to_idle_que(this); } private: ThreadPool * hr_pool_; ProcCallBack cb_func_; void* param_; };
线程池实现的关键是如何创建多个线程,并且当任务来临时可以从线程池中取一个线程(也就是去得到其中一个线程的指针),然后提交任务并执行。还有一点就是 当任务执行完后,应该将该线程句柄重新加入到空闲线程队列,所以我们将ThreadPool的指针传入给了WorkThread,thr_pool_最后 可以调用move_to_idle_que(this)来将该线程句柄移到空闲队列中。
ThreadPool中一些关键代码的实现:
#include"threadpool.h" #include<cstdio> class ThreadPool; int ThreadPool::start_thread_pool(size_t thread_num) { thr_num_ = thread_num; int ret = 0; for (size_t i = 0; i < thr_num_; ++i) { WorkThread* pthr = new WorkThread(this); //pthr->set_thread_id(i); if ((ret = pthr->hr_pool_->start_thread_pool()) != 0) { printf("start_thread_pool: failed when create a work thread: %d\n", i); delete pthr; return i; } append_idle_thread(pthr); } return thr_num_; } int ThreadPool::stop_thread_pool() { for (size_t i = 0; i < thr_vec_.size(); ++i) { WorkThread* pthr = thr_vec_[i]; //pthr->join(); delete pthr; } thr_vec_.clear(); idle_que_.clear(); return 0; } void ThreadPool::destroy() { stop_thread_pool(); } void ThreadPool::append_idle_thread(WorkThread* pthread) { thr_vec_.push_back(pthread); idle_que_.push_back(pthread); } void ThreadPool::move_to_idle_que(WorkThread* idlethread) { idle_que_.push_back(idlethread); } WorkThread* ThreadPool::get_idle_thread() { WorkThread* pthr = NULL; if (!idle_que_.empty()) { vector<WorkThread*>::iterator it = idle_que_.end(); pthr = *it; idle_que_.pop_back(); } return pthr; } void ThreadPool::post_job(ProcCallBack func, void* data) { WorkThread* pthr = get_idle_thread(); while (pthr == NULL) { //Sleep(500000); pthr = get_idle_thread(); } pthr->set_job(func, data); } void count(void* param) { // do some your work, like: int* pi = static_cast<int*>(param); int val = *pi + 1; printf("val=%d\n", val); delete pi; } int main() { //程序中使用如下: ThreadPool* ptp = new ThreadPool(); ptp->start_thread_pool(3); //启动3 个线程 ptp->post_job(count, new int(1)); //提交任务 ptp->post_job(count, new int(2)); ptp->post_job(count, new int(3)); //程序线束时 ptp->stop_thread_pool(); return 0; }
时间: 2024-10-24 15:00:16