TinywebServer代码详解–半同步半反应堆线程池-下(3)
该blog内容转自:最新版Web服务器项目详解 - 02 半同步半反应堆线程池(下)
该blog对上述内容进行补充(在本人的角度)
原项目地址(点击跳转)
博主添加注释后项目地址(点击跳转)
一、基础知识
1.静态成员变量
将类成员变量声明为static,则为静态成员变量,与一般的成员变量不同,无论建立多少对象,都只有一个静态成员变量的拷贝,静态成员变量属于一个类,所有对象共享
静态变量在编译阶段就分配了空间,对象还没创建时就已经分配了空间,放到全局静态区
静态成员变量注意事项:
- 最好是类内声明,类外初始化(以免类名访问静态成员访问不到)
- 无论公有,私有,静态成员都可以在类外定义,但私有成员仍有访问权限
- 非静态成员类外不能初始化
- 静态成员数据是共享的
2.静态成员函数
将类成员函数声明为static,则为静态成员函数
- 静态成员函数可以直接访问静态成员变量,不能直接访问普通成员变量,但可以通过参数传递的方式访问
- 普通成员函数可以访问普通成员变量,也可以访问静态成员变量
- 静态成员函数没有this指针。非静态数据成员为对象单独维护,但静态成员函数为共享函数,无法区分是哪个对象,因此不能直接访问普通变量成员,也没有this指针
3.pthread_create陷阱
1 2 3 4 5
| #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
|
参数
pthread_t *thread
:指向线程标识符的指针。成功创建线程后,该标识符会被填充
const pthread_attr_t *attr
:线程属性对象。可以为 NULL
,表示使用默认属性
void *(*start_routine) (void *)
:指向线程函数的指针。当线程启动时,将调用这个函数
void *arg
:传递给线程函数的参数
返回值
函数原型中的第三个参数,为函数指针,指向处理线程函数的地址。该函数,要求为静态函数。如果处理线程函数为类成员函数时,需要将其设置为静态成员函数。
(1)this指针的锅
pthread_create的函数原型中第三个参数的类型为函数指针,指向的线程处理函数参数类型为(void *)
,若线程函数为类成员函数,则this指针会作为默认的参数被传进函数中,从而和线程函数参数(void*)
不能匹配,不能通过编译。
静态成员函数就没有这个问题,里面没有this指针。
二、线程池代码解析
线程池的设计模式为半同步/半反应堆,其中反应堆具体为Proactor事件处理模式
具体的,主线程为异步线程,负责监听文件描述符,接收socket新连接,若当前监听的socket发生了读写事件,主线程从socket上接收数据,然后将任务插入到请求队列。工作线程从请求队列中取出任务,完成读写数据的处理
1.线程池的定义
threadpool/threadpool.h
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
| #pragma once
#include <list> #include <cstdio> #include <exception> #include <pthread.h> #include "../lock/locker.h" #include "../CGImysql/sql_connection_pool.h"
template <typename T> class threadpool { public: threadpool(int actor_model, connection_pool *connPool, int thread_number = 8, int max_request = 10000); ~threadpool(); bool append(T *request, int state); bool append_p(T *request);
private: static void *worker(void *arg); void run(); shen private: int m_thread_number; int m_max_requests; pthread_t *m_threads; std::list<T *> m_workqueue; locker m_queuelocker; sem m_queuestat; connection_pool *m_connPool; int m_actor_model; };
|
2.线程池创建与回收
构造函数中创建线程池,pthread_create
函数中将类的对象作为参数传递给静态函数(worker
),在静态函数中引用这个对象,并调用其动态方法(run
)
具体的,类对象传递时用this
指针,传递给静态函数后,将其转换为线程池类,并调用私有成员函数run
线程池中每一个工作线程,创建之后,均会对其进行线程分离
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
|
template <typename T> threadpool<T>::threadpool( int actor_model, connection_pool *connPool, int thread_number, int max_requests) : m_actor_model(actor_model),m_thread_number(thread_number), m_max_requests(max_requests), m_threads(NULL),m_connPool(connPool) { if (thread_number <= 0 || max_requests <= 0) throw std::exception();
m_threads = new pthread_t[m_thread_number]; if (!m_threads) throw std::exception();
for (int i = 0; i < thread_number; ++i) { if (pthread_create(m_threads + i, NULL, worker, this) != 0) { delete[] m_threads; throw std::exception(); } if (pthread_detach(m_threads[i])) { delete[] m_threads; throw std::exception(); } } }
|
3.向请求队列中添加任务
通过list
容器创建请求队列,向队列中添加任务(http
请求的连接对象)时,通过互斥锁保证线程安全,添加完成后通过信号量提醒有任务要处理,最后注意线程同步
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
|
template <typename T> bool threadpool<T>::append(T *request, int state) { m_queuelocker.lock(); if (m_workqueue.size() >= m_max_requests) { m_queuelocker.unlock(); return false; } request->m_state = state; m_workqueue.push_back(request); m_queuelocker.unlock(); m_queuestat.post(); return true; }
template <typename T> bool threadpool<T>::append_p(T *request) { m_queuelocker.lock(); if (m_workqueue.size() >= m_max_requests) { m_queuelocker.unlock(); return false; } m_workqueue.push_back(request); m_queuelocker.unlock(); m_queuestat.post(); return true; }
|
4.线程处理函数
内部访问私有成员函数run,完成线程处理要求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
template <typename T> void *threadpool<T>::worker(void *arg) { threadpool *pool = (threadpool *)arg; pool->run(); return pool; }
|
5.run执行任务
主要实现,工作线程从请求队列中取出某个任务进行处理,注意线程同步。并且根据当前服务器事件处理模式,执行子线程响应的任务。
Reactor模式
- 线程池工作线程,需要负责IO数据读写以及数据的分析处理(业务处理)
Proactor模式
- 线程池工作线程,仅需要负责数据的分析处理(业务处理)
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
|
template <typename T> void threadpool<T>::run() { while(true) { m_queueStat.wait(); m_queueLocker.lock(); if(m_workqueue.empty()) { m_queuelocker.unlock(); continue; } T *request = m_workQueue.front(); m_workQueue.pop_front(); m_queueLocker.unlock(); if(!request){ continue; }
if(1 == m_actor_model) { if(0 == request->m_state) { if(request->read_once()) { request->improv = 1; connectionRAII mysqlcon(&request->mysql,m_connPool); request->process(); } else { request->improv = 1; request->timer_flag = 1; } } else { if(request->write()) { request->improv = 1; } else { request->improv = 1; request->timer_flag = 1; } } }
else { connectionRAII mysqlcon(&request->mysql, m_connPool); request->process(); } } }
|