• 【Linux学习】高并发服务器框架 线程池介绍+线程池封装


    目录

    前言

    一、线程池 

    💻线程池基本概念

    💻线程池组成部分

    💻线程池工作原理 

    二、线程池代码封装

    🌈main.cpp

    🌈ThreadPool.h

    🌈ThreadPool.cpp

    🌈ChildTask.h 

    🌈ChildTask.cpp

    🌈BaseTask.h

    🌈BaseTask.cpp

    三、测试效果


    前言

    本文主要学习Linux内核编程,结合Visual Studio 2019进行跨平台编程,内容包括线程池介绍以及线程池封装

    一、线程池 

    💻线程池基本概念

    • 线程池是预先创建线程的一种技术 (服务器真正意义上实现高并发就必须用线程池)
    • 🌰举个例子:生活中的水池,是装东西的容器,用来装水的,线程池当然就是拿来装线程的
    • 线程池在任务还没有到来之前,创建一定数量的线程,放入空闲队列中,这些线程都是处于阻塞状态,不消耗CPU,但占用较小的内存空间
    • 当新任务到来时,缓冲池选择一个空闲线程,把任务传入此线程中运行,如果缓冲池已经没有空闲线程,则新建若干个线程,当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源

    💻线程池组成部分

    • 线程池类

            维护工作者线程队列(包括空闲与忙碌队列)

            维护一个任务队列

            维护一个线程池调度器指针

    • 线程池调度器(本身也是一个线程)

            负责线程调度

            负责任务分配

    • 工作者线程类(线程池中的线程类的封装)
    • 任务队列
    • 任务接口(实际的业务逻辑都继承自该接口)

    💻线程池工作原理 

    根据服务器的需要,来设置线程的数量,可能是10条、20条、30条,根据服务器的承载,10条不代表只能做10个任务,总有任务做的快,有的做的慢,可能可以完成20个任务 

    🌰举个例子:如上动图所示,当我们服务器收到一个注册业务,是一个服务器要执行的任务,它会进入到任务队列,队列先进先出,顺次执行,任务会唤醒空闲列表当中的一个空闲的线程,接到任务之后,空闲线程会从空闲列表中消失,进入到忙碌列表,去完成对应的任务,完成任务后,从忙碌列表中出去,到空闲列表继续等待新任务

    如果,所有的线程都在忙,都在做任务,这时候登录进来,先进入任务队列,会创建一个新的线程来接这个任务,当所有线程都完成任务,回到空闲列表后,新创建的线程销毁,留下原先设置的对应数量线程(类似,保留老员工,把实习生裁员)

    • 队列:先进先出
    • 空闲列表(链表):不定长(有的时候可能需要创建新线程来接任务)
    • 忙碌列表(链表):不定长(有的时候可能需要创建新线程来接任务)

    二、线程池代码封装

    🌈main.cpp

    1. #include <iostream>
    2. #include "ThreadPool.h"
    3. #include "ChildTask.h"
    4. using namespace std;
    5. int main()
    6. {
    7. ThreadPool* pool = new ThreadPool(10);//10条线程
    8. for (int i = 0; i < 30; i++)//30个任务
    9. {
    10. char buf[40] = { 0 };//初始化
    11. sprintf(buf, "%s%d","任务", i);
    12. BaseTask* task = new ChildTask(buf);
    13. pool->pushTask(task);
    14. }
    15. //方便测试,设置死循环,让线程不退出
    16. while (1)
    17. {
    18. }
    19. return 0;
    20. }

    🌈ThreadPool.h

    1. #pragma once
    2. #include <queue>//队列
    3. #include <list>//链表头文件
    4. #include <pthread.h>//线程头文件
    5. #include "BaseTask.h"
    6. #include <algorithm>//find的包
    7. #include <iostream>
    8. using namespace std;
    9. #define MIN_NUM 10//最小值 默认参数
    10. class ThreadPool
    11. {
    12. public:
    13. ThreadPool(const int num = MIN_NUM);
    14. ~ThreadPool();
    15. //判断队列是否为空
    16. bool QueueIsEmpty();
    17. //线程互斥量加锁解锁
    18. void Lock();//加锁
    19. void Unlock();//解锁
    20. //线程条件变量等待和唤醒
    21. void Wait();//等待
    22. void WakeUp();//唤醒
    23. //添加任务到任务队列
    24. void pushTask(BaseTask* task);
    25. //从任务队列移除任务
    26. BaseTask* popTask(BaseTask* task);
    27. //从忙碌回到空闲(状态:工作结束)
    28. void MoveToIdle(pthread_t id);
    29. //从空闲到忙碌(状态:工作开始)
    30. void MoveToBusy(pthread_t id);
    31. //线程执行函数(静态)
    32. static void* RunTime(void* vo);
    33. private:
    34. int threadMinNum;//最大线程数量
    35. int threadMaxNum;//最小线程数量
    36. queue<BaseTask*>taskQueue;//任务队列
    37. list<pthread_t>busyList;//忙碌列表
    38. list<pthread_t>idleList;//空闲列表
    39. pthread_cond_t cond;//条件变量,目的:让线程等待或者唤醒
    40. pthread_mutex_t mutex;//互斥量,目的:做锁
    41. };

    🌈ThreadPool.cpp

    1. #include "ThreadPool.h"
    2. ThreadPool::ThreadPool(const int num)
    3. {
    4. this->threadMaxNum = num;
    5. //条件变量、互斥量初始化
    6. pthread_mutex_init(&this->mutex, NULL);//初始化
    7. pthread_cond_init(&this->cond, NULL);//初始化
    8. pthread_t id;
    9. //线程num条创建
    10. for (int i = 0; i < this->threadMinNum; i++)
    11. {
    12. //线程创建
    13. pthread_create(&id, NULL, RunTime, this);
    14. this->idleList.push_back(id);//线程存入空闲列表
    15. }
    16. }
    17. ThreadPool::~ThreadPool()
    18. {
    19. }
    20. //判断任务队列是否为空
    21. bool ThreadPool::QueueIsEmpty()
    22. {
    23. return this->taskQueue.empty();
    24. }
    25. //线程加锁
    26. void ThreadPool::Lock()
    27. {
    28. pthread_mutex_lock(&this->mutex);
    29. }
    30. //线程解锁
    31. void ThreadPool::Unlock()
    32. {
    33. pthread_mutex_unlock(&this->mutex);
    34. }
    35. //线程等待
    36. void ThreadPool::Wait()
    37. {
    38. pthread_cond_wait(&this->cond, &this->mutex);
    39. }
    40. //线程唤醒
    41. void ThreadPool::WakeUp()
    42. {
    43. pthread_cond_signal(&this->cond);
    44. }
    45. //添加任务到任务队列
    46. void ThreadPool::pushTask(BaseTask* task)
    47. {
    48. Lock();
    49. taskQueue.push(task);
    50. Unlock();
    51. WakeUp();
    52. }
    53. //从任务队列移除任务
    54. BaseTask* ThreadPool::popTask(BaseTask* task)
    55. {
    56. task = this->taskQueue.front();//从队列头取
    57. this->taskQueue.pop();//删除队列头
    58. return task;
    59. }
    60. //从忙碌回到空闲(状态:工作结束)
    61. void ThreadPool::MoveToIdle(pthread_t id)
    62. {
    63. list<pthread_t>::iterator iter;
    64. //查找忙碌队列中的线程
    65. iter = find(busyList.begin(), busyList.end(), id);//find查找
    66. if (iter != busyList.end())
    67. {
    68. //从忙碌到移除
    69. this->busyList.erase(iter);
    70. //添加到空闲
    71. this->idleList.push_back(*iter);//*iter
    72. }
    73. }
    74. //从空闲到忙碌(状态:工作开始)
    75. void ThreadPool::MoveToBusy(pthread_t id)
    76. {
    77. list<pthread_t>::iterator iter;
    78. //查找空闲队列中的线程
    79. iter = find(idleList.begin(), idleList.end(), id);//find查找
    80. if (iter != idleList.end())
    81. {
    82. //从空闲移除
    83. this->idleList.erase(iter);
    84. //添加到忙碌
    85. this->busyList.push_back(*iter);//*iter
    86. }
    87. }
    88. //线程执行函数
    89. void* ThreadPool::RunTime(void* vo)
    90. {
    91. //拿到执行线程自己的id 因为后面要处理忙碌和空闲的情况
    92. pthread_t id = pthread_self();
    93. //确保主线程和子线程分离,子线程结束后,资源自动回收
    94. pthread_detach(id);
    95. //线程参数获取
    96. ThreadPool* argThis = (ThreadPool*)vo;
    97. while (true)
    98. {
    99. argThis->Lock();
    100. //如果任务队列为空 线程则一直等待
    101. //知道任务队列不为空则被pushTask函数唤醒线程
    102. while (argThis->QueueIsEmpty())
    103. {
    104. argThis->Wait();
    105. }
    106. argThis->MoveToBusy(id);
    107. cout << "工作前 任务数:" << argThis->taskQueue.size() << endl;
    108. cout << "工作前 busy:" << argThis->busyList.size() << endl;
    109. cout << "工作前 idle:" << argThis->idleList.size() << endl;
    110. cout << "-----------------------------------------" << endl;
    111. //取任务
    112. BaseTask* task;
    113. task = argThis->popTask(task);
    114. argThis->Unlock();
    115. //任务工作
    116. task->working();
    117. //工作结束
    118. argThis->Lock();
    119. argThis->MoveToIdle(id);
    120. argThis->Unlock();
    121. cout << "工作完 任务数:" << argThis->taskQueue.size() << endl;
    122. cout << "工作完 busy:" << argThis->busyList.size() << endl;
    123. cout << "工作完 idle:" << argThis->idleList.size() << endl;
    124. cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << endl;
    125. }
    126. return nullptr;
    127. }

    🌈ChildTask.h 

    1. #pragma once
    2. #include "BaseTask.h"
    3. #include <iostream>
    4. #include <unistd.h> //sleep头文件
    5. using namespace std;
    6. class ChildTask :
    7. public BaseTask
    8. {
    9. public:
    10. ChildTask(char* data);
    11. ~ChildTask();
    12. void working();
    13. };

    🌈ChildTask.cpp

    1. #include "ChildTask.h"
    2. ChildTask::ChildTask(char* data):BaseTask(data)//参数传给父类
    3. {
    4. }
    5. ChildTask::~ChildTask()
    6. {
    7. }
    8. void ChildTask::working()
    9. {
    10. cout <<"正在执行ing"<< this->data << endl;//打印
    11. sleep(3);//延时3秒 (模拟做业务的时间比较长)
    12. }

    🌈BaseTask.h

    1. #pragma once
    2. #include <string.h>
    3. class BaseTask
    4. {
    5. public:
    6. BaseTask(char * data);
    7. ~BaseTask();
    8. char data[1024]; //装业务
    9. virtual void working() = 0;//虚函数
    10. };

    🌈BaseTask.cpp

    1. #include "BaseTask.h"
    2. BaseTask::BaseTask(char* data)
    3. {
    4. bzero(this->data, sizeof(this->data));//清空
    5. memcpy(this->data, data, sizeof(data));
    6. }
    7. BaseTask::~BaseTask()
    8. {
    9. delete this->data;//清除释放
    10. }

    三、测试效果

    通过Linux连接VS进行跨平台编程,我们可以清晰的看到有几个线程是在做任务,几个线程是空闲的,整个过程就很清晰直观的展现出来了,如下动图所示: 

    以上就是本文的全部内容啦!如果对您有帮助,麻烦点赞啦!收藏啦!欢迎各位评论区留言!!!

  • 相关阅读:
    Node.js中基于node-schedule实现定时任务之详解
    各种锁机制
    优化大型机床装配调度:项目管理软件的应用方法
    Robust Lane Detection from Continuous Driving
    Java并发之volatile关键字内存可见性问题
    微信小程序python+django+uniapp公交路线查询系统
    内存取证之NSSCTF-OtterCTF 2018(复现赛)
    Linux From Scratch 11.2 发布
    修改jar包中的class文件
    基于B/S架构,包括PC后台管理端、APP移动端、可视化大屏端的智慧工地源码
  • 原文地址:https://blog.csdn.net/m0_61745661/article/details/125560959