线程池和线程池版本的服务器代码 Linux网络通信( 三 )

运行结果如下:

线程池和线程池版本的服务器代码 Linux网络通信

文章插图
可以看到线程最多处理三个任务,而任务队列中最多可以存在20个任务 , 当线程取走了任务之后 , 唤醒生产者继续添加任务 。
版本二首先封装一个任务:
class Task{public:Task(int a = 0, int b = 0):_a(a),_b(b){}void Run(){//执行的任务可以自己编写}private:int _a;int _b;};线程池的主要代码框架(唤醒和等待操作都已经封装好):
#define DEFAULT_MAX_PTHREAD 5class ThreadPool{public:ThreadPool(int max_pthread = DEFAULT_MAX_PTHREAD):_max_thread(max_pthread){}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}public:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnlockQueue(){pthread_mutex_unlock(&_mutex);}void ThreadWait(){pthread_cond_wait(&_cond, &_mutex);}void WakeUpThread(){pthread_cond_signal(&_cond);//pthread_cond_broadcast(&_cond);}bool IsEmpty(){return _q.empty();}private:queue<Task*>_q;int_max_thread;pthread_mutex_t _mutex;pthread_cond_t_cond;};创建多个线程创建多个线程可以用一个循环进行创建 。需要注意的是,创建一个线程还需要提供一个线程启动后要执行的函数 , 这个启动函数只能有一个参数 。如果把这个函数设置为成员函数,那么这个函数的第一个参数默认是this指针,这样显然是不可行的 , 所以这里我们考虑把这个启动函数设置为静态的 。但是设置为静态的成员函数又会面临一个问题:如何调用其他成员函数和成员变量? 所以这里我们考虑创建线程的时候,把this指针传过去 , 让启动函数的arg 参数去接收即可
static void* Runtine(void* arg){pthread_detach(pthread_self());ThreadPool* this_p = (ThreadPool*)arg;while (1){this_p->LockQueue();while (this_p->IsEmpty()){this_p->ThreadWait();}Task* t;this_p->Get(t);this_p->UnlockQueue();// 解锁后处理任务t->Run();delete t;}}void ThreadPoolInit(){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);pthread_t t[_max_thread];for(int i = 0; i < _max_thread; ++i){pthread_create(t + i, nullptr, Runtine, this);}}注意: 线程创建后,执行启动函数,在这个函数中,线程会去任务队列中取任务并处理,取任务前需要进行加锁的操作(如果队列为空需要挂起等待),取完任务然后进行解锁,然后处理任务 , 让其它线程去任务队列中取任务
放任务: 主线程无脑往任务队列中塞任务,塞任务之前进行加锁,塞完任务解锁,然后唤醒在条件变量下等待的队列取任务: 线程池中的线程从任务队列中取任务,这里不需要加锁,因为这个动作在启动函数中加锁的那一段区间中被调用的,其实已经上锁了
// 放任务void Put(Task* data){LockQueue();_q.push(data);UnlockQueue();WakeUpThread();}// 取任务void Get(Task*& data){data = https://www.huyubaike.com/biancheng/_q.front();_q.pop();}这两个版本都可以实现简易的线程池,下面线程池版本的服务器主要是用版本二来实现,因为版本一要修改的内容有点多 , 小伙伴们可以自己修改一下
线程池版本服务器多线程版本效果看起来还不错,但是来一个连接就创建一个线程,断开一个连接就释放一个线程,这样频繁地创建和释放线程资源,对OS来说是一种负担 , 同时也带来资源的浪费,如果我们使用线程池,把每一个客户端连接封装成一个任务,让线程池去处理,这样就不需要频繁地创建和销毁消除,效率也能提升很多 。线程池采用版本二,代码如下:
#pragma once#include <iostream>#include <queue>#include <pthread.h>#include <unistd.h>#include "Task.hpp"#define DEFAULT_MAX_PTHREAD 5class ThreadPool{public:ThreadPool(int max_pthread = DEFAULT_MAX_PTHREAD):_max_thread(max_pthread){}static void* Runtine(void* arg){pthread_detach(pthread_self());ThreadPool* this_p = (ThreadPool*)arg;while (1){this_p->LockQueue();while (this_p->IsEmpty()){this_p->ThreadWait();}Task* t;this_p->Get(t);this_p->UnlockQueue();// 解锁后处理任务t->Run();delete t;}}void ThreadPoolInit(){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);pthread_t t[_max_thread];for(int i = 0; i < _max_thread; ++i){pthread_create(t + i, nullptr, Runtine, this);}}void Put(Task* data){LockQueue();_q.push(data);UnlockQueue();WakeUpThread();}void Get(Task*& data){data = https://www.huyubaike.com/biancheng/_q.front();_q.pop();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}public:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnlockQueue(){pthread_mutex_unlock(&_mutex);}void ThreadWait(){pthread_cond_wait(&_cond, &_mutex);}void WakeUpThread(){pthread_cond_signal(&_cond);//pthread_cond_broadcast(&_cond);}bool IsEmpty(){return _q.empty();}private:std::queue

推荐阅读