14业务逻辑-线程池处理消息队列
一、线程池处理业务逻辑的必要性
业务逻辑层面,这个就要用多线程处理,所谓业务逻辑:充值,抽卡,战斗;
对于充值,需要本服务器和专门的充值服务器通讯,一般需要数秒到数十秒的通讯时间。此时,必须采用多线程【100个多线程】处理方式,保证用户的需求能够得到及时处理。
线程池好处:
提前创建好一堆线程,并搞一个类来统一管理和调度这一堆线程【这一堆线程我们就叫做线程池】
当来了一个任务【来了一个消息】的时候,我从这一堆线程中找一个空闲的线程去做这个任务【去干活/去处理这个消息】,活干完之后,我这个线程里边有一个循环语句,我可以循环回来等待新任务,再有新任务的时候再去执行新的任务;就好像这个线程可以回收再利用 一样。
----a)实现创建好一堆线程,避免动态创建线程来执行任务,提高了程序的稳定性;有效的规避程序运行之中创建线程有可能失败的风险;
----b)提高程序运行效率:线程池中的线程,反复循环再利用;
二、线程池结构
2.1线程池的初始化
线程池类对象的实例化(线程池的管理者):
CThreadPool g_threadpool; //线程池全局对象
-------------------------------
class CThreadPool
{
public:
//构造函数
CThreadPool();
//析构函数
~CThreadPool();
public:
bool Create(int threadNum); //创建该线程池中的所有线程
void StopAll(); //使线程池中的所有线程退出
void Call(int irmqc); //来任务了,调一个线程池中的线程下来干活
private:
static void* ThreadFunc(void *threadData); //新线程的线程回调函数,静态函数是属于类的
private:
//定义一个 线程池中的 线程 的结构,以后可能做一些统计之类的 功能扩展,所以引入这么个结构来 代表线程 感觉更方便一些;
//线程有关的信息 ====================================== 一条线程的信息结构
struct ThreadItem
{
pthread_t _Handle; //线程句柄
CThreadPool *_pThis; //记录线程池的指针 ================???未完全理解
bool ifrunning; //标记是否正式启动起来,启动起来后,才允许调用StopAll()来释放
//构造函数
ThreadItem(CThreadPool *pthis):_pThis(pthis),ifrunning(false){}
//析构函数
~ThreadItem(){}
};
private:
static pthread_mutex_t m_pthreadMutex; //线程同步互斥量/也叫线程同步锁
static pthread_cond_t m_pthreadCond; //线程同步条件变量
static bool m_shutdown; //线程退出标志,false不退出,true退出
int m_iThreadNum; //要创建的线程数量
//int m_iRunningThreadNum; //线程数, 运行中的线程数
std::atomic<int> m_iRunningThreadNum; //线程数, 运行中的线程数,原子操作
time_t m_iLastEmgTime; //上次发生线程不够用【紧急事件】的时间,防止日志报的太频繁
//time_t m_iPrintInfoTime; //打印信息的一个间隔时间,我准备10秒打印出一些信息供参考和调试
//time_t m_iCurrTime; //当前时间
std::vector<ThreadItem *> m_threadVector; //线程 容器,容器里就是各个线程了
};
线程池的创建和初始化等待:
bool CThreadPool::Create(int threadNum)
它的被调用位置:`ngx_worker_process_init(int inum)`子进程创建时的初始化
//在Create()中有pthread_create()函数
err = pthread_create(&pNew->_Handle, NULL, ThreadFunc, pNew);
//线程是循环创建的,创建完立即执行ThreadFunc(),线程入口函数
PS:细节,在创建inum个线程时,必须保证所有线程都进入线程等待的状态,才能够让(主线程)Create()返回。判断方法:(*iter)->ifrunning
void* CThreadPool::ThreadFunc(void* threadData)
//有两层while循环,外面一层是while(true)控制线程池的线程执行完业务之后的循环利用,而里面一层的是为了辅助线程等待和取消息队列的作用。
{
ThreadItem *pThread = static_cast<ThreadItem*>(threadData);
CThreadPool *pThreadPoolObj = pThread->_pThis;
char *jobbuf = NULL;
CMemory *p_memory = CMemory::GetInstance();
int err;
pthread_t tid = pthread_self();
while(true)
{
err = pthread_mutex_lock(&m_pthreadMutex);
if(err != 0) ngx_log_stderr(err,"CThreadPool::ThreadFunc()pthread_mutex_lock()失败,返回的错误码为%d!",err);
while( (jobbuf = g_socket.outMsgRecvQueue()) == NULL && m_shutdown == false)
{
if(pThread->ifrunning == false)
pThread->ifrunning = true;
pthread_cond_wait(&m_pthreadCond, &m_pthreadMutex); //整个服务器程序刚初始化的时候,所有线程必然是卡在这里等待的;
}
err = pthread_mutex_unlock(&m_pthreadMutex);
if(err != 0) ngx_log_stderr(err,"CThreadPool::ThreadFunc()pthread_cond_wait()失败,返回的错误码为%d!",err);
//先判断线程退出这个条件
if(m_shutdown)
{
if(jobbuf != NULL)
{
p_memory->FreeMemory(jobbuf);
}
break;
}
++pThreadPoolObj->m_iRunningThreadNum;
ngx_log_stderr(0,"执行开始---begin,tid=%ui!",tid);
sleep(5); //临时测试代码
ngx_log_stderr(0,"执行结束---end,tid=%ui!",tid);
p_memory->FreeMemory(jobbuf);
--pThreadPoolObj->m_iRunningThreadNum;
} //end while(true)
return (void*)0;
}
要点解释:
1.在消息队列中没有消息时,所有线程都在pthread_cond_wait(&m_pthreadCond, &m_pthreadMutex);的位置等待。
2.pthread_cond_wait(),在等待的过程中,会放开互斥锁,给其他线程,当收到pthread_cond_signal(&m_pthreadCond);的条件变量的激活时,会继续执行pthread_cond_wait后面的代码,不过在此之前。原先放开的互斥锁有重新锁住【pthread_cond_wait()返回时,互斥量再次被锁住】。
3.调用一次pthread_cond_signal()可能会唤醒多个在pthread_cond_wait()等待的线程【惊群】,互斥量再次被锁住这是第一道墙,还有就是内层的while( (jobbuf = g_socket.outMsgRecvQueue()) == NULL && m_shutdown == false),会再一次判断,只有没取到消息,那么就在待到内层的等待中,只有取到的消息,才能够跳出内层的while。
4.跳出了内层的循环之后,就可以执行业务逻辑的工作。
2.2线程池的激发
void CSocekt::ngx_wait_request_handler_proc_plast(lpngx_connection_t c)//包的最后阶段处理
----void CThreadPool::Call(int irmqc)//"激发"线程池中的某个线程来处理业务逻辑,让线程池开始做事了
--------int err = pthread_cond_signal(&m_pthreadCond);//条件变量的激发函数

浙公网安备 33010602011771号