运行结果如下:

文章插图
可以看到线程最多处理三个任务,而任务队列中最多可以存在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
经验总结扩展阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 京口和瓜州是现在的哪个城市
- 明日方舟2022夏日嘉年华限定卡池有哪些角色
- 华为p50是曲面屏吗_直屏和曲屏有什么区别
- 馅饼的做法怎么和面
- 2023年9月7日开鱼池吉日一览表 2023年9月7日开鱼池行吗
- 2023年9月7日挖掘池塘吉日一览表 2023年9月7日适合挖掘池塘吗
- 孩子沉迷于游戏和手机,作父母的该怎么办(孩子沉迷游戏爸妈束手无策)
- 2023年9月7日是建蓄水池吉日吗 2023年农历七月廿三宜建蓄水池吗
- 2023年9月7日是池塘放水吉日吗 2023年9月7日适合池塘放水吗
- 蜂鸟的寓意和象征