• linux线程池


    线程池: * 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着 监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利 用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

     线程池的应用场景:

    * 1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技 术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个 Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

    * 2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

    * 3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情 况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限, 出现错误.

     线程池的种类:

    * 线程池示例:

    * 1. 创建固定数量线程池,循环从任务队列中获取任务对象,

    * 2. 获取到任务对象后,执行任务对象中的任务接口  

    代码的大概整体结构:

     

    main.cc

    1. #include "threadpool.hpp"
    2. #include "task.hpp"
    3. int main(){
    4. threadpool<task>* tp=new threadpool<task>();
    5. //在使用模板类时,始终需要提供具体的类型参数,这样编译器才能正确生成类型安全的代码。如果不加类型参数,将导致编译错误。
    6. tp->init();
    7. tp->start();
    8. int cnt=10;
    9. while(cnt){
    10. //向线程池推任务
    11. sleep(1);
    12. task t(1,1);
    13. tp->equeue(t);
    14. sleep(1);
    15. cout<<"cnt: "<<cnt--<<endl;
    16. }
    17. tp->stop();
    18. cout<<"stop!!!!!!!"<<endl;
    19. sleep(10);
    20. return 0;
    21. }

    task.hpp

    1. #pragma once
    2. #include
    3. #include
    4. using namespace std;
    5. class task
    6. {
    7. public:
    8. task() {}
    9. task(int x, int y) : _x(x), _y(y)
    10. {
    11. }
    12. void excute()
    13. {
    14. _result = _x + _y;
    15. }
    16. void operator()(){
    17. excute();
    18. }
    19. string debug()
    20. {
    21. string msg = to_string(_x) + "+" + to_string(_y) + "=?";
    22. return msg;
    23. }
    24. string result()
    25. {
    26. string msg = to_string(_x) + "+" + to_string(_y) +"="+ to_string(_result);
    27. return msg;
    28. }
    29. ~task()
    30. {
    31. }
    32. private:
    33. int _x;
    34. int _y;
    35. int _result;
    36. };

    thread.hpp

    1. #pragma once
    2. #include <iostream>
    3. #include <string>
    4. #include <pthread.h>
    5. #include <functional> //回调方法
    6. using namespace std;
    7. namespace threadmodel // 构造一个命名空间
    8. {
    9. //typedef void (*func_t)(threaddate *td);
    10. // typedef void (*func_t)(const string &name); // func_t 是一个指向返回类型为 void 且有参数的函数的指针。
    11. // // func_t 现在可以用作一个函数指针类型的别名,表示任何指向带字符串参数且返回类型为 void 的函数的指针。
    12. using func_t=function<void(const string&)>;//返回值为void,参数为空的函数类型
    13. class thread
    14. {
    15. public:
    16. void excute()
    17. {
    18. cout << _name << " is running ! " << endl;
    19. _running = true;
    20. _func(_name); // 回调不仅回调运行,他还得结束
    21. _running = false; // 回调完就结束了
    22. }
    23. public:
    24. thread(const string &name, func_t func) : _name(name), _func(func)
    25. {
    26. cout << "create: " << name << " done! " << endl;
    27. }
    28. static void *threadroutine(void *agv) // 只要线程启动,新线程都会启动这个方法
    29. { // 因为是类内定义的方法,所以会隐含一个this指针参数,加了static就可以,这是因为 static 成员函数不与类的实例相关联,因此它不需要 this 指针。
    30. thread *self = static_cast<thread *>(agv); // 获得当前对象。因为要调用_func,但_func是动态的,静态函数无法访问所以传this指针访问
    31. self->excute();
    32. return nullptr;
    33. }
    34. bool start()
    35. { // 线程启动方法
    36. int n = ::pthread_create(&_tid, nullptr, threadroutine, this);
    37. // 使用::pthread_create确保调用的是全局命名空间中的pthread_create函数,避免当前命名空间内可能存在的同名函数的影响。
    38. // 直接使用 pthread_create 会根据当前命名空间查找,如果找到了同名函数,就会调用那个函数。
    39. if (n != 0)
    40. return false;
    41. return true;
    42. }
    43. string status()
    44. {
    45. if (_running)
    46. {
    47. return "running";
    48. }
    49. else
    50. return "sleep";
    51. }
    52. void stop()
    53. { // 线程停止方法
    54. if (_running)
    55. { // 得先有线程才能停止
    56. _running = false; // 状态停止
    57. ::pthread_cancel(_tid);
    58. cout << _name << " stop ! " << endl;
    59. }
    60. }
    61. void join()
    62. { // 线程等待方法
    63. if (!_running)
    64. { // 没有running才值得join
    65. ::pthread_join(_tid, nullptr);
    66. cout << _name << " join ! " << endl;
    67. }
    68. }
    69. string threadname()
    70. {
    71. return _name;
    72. }
    73. ~thread()
    74. {
    75. }
    76. private:
    77. string _name; // 线程的名字
    78. pthread_t _tid; // 线程的id
    79. bool _running; // 是否处于工作状态
    80. func_t _func; // 线程要执行的回调函数
    81. };
    82. };

    threadpool.hpp 

    1. #pragma once
    2. #include <iostream>
    3. #include <unistd.h>
    4. #include <string>
    5. #include <vector>
    6. #include <queue>
    7. #include <functional>
    8. #include "thread.hpp"
    9. using namespace std;
    10. using namespace threadmodel;
    11. static const int faultnum = 5;
    12. void test()
    13. {
    14. while (true)
    15. {
    16. cout << "hello world" << endl;
    17. sleep(1);
    18. }
    19. }
    20. template <typename T>
    21. class threadpool
    22. {
    23. private:
    24. void lockqueue()
    25. {
    26. pthread_mutex_lock(&_mutex);
    27. }
    28. void unlockqueue()
    29. {
    30. pthread_mutex_unlock(&_mutex);
    31. }
    32. void wakeup()
    33. {
    34. pthread_cond_signal(&_cond); // 唤醒一个
    35. }
    36. void wakeupall()
    37. {
    38. pthread_cond_broadcast(&_cond); // 唤醒全部
    39. }
    40. void Sleep()
    41. {
    42. pthread_cond_wait(&_cond, &_mutex); // 等待
    43. }
    44. bool isempty()
    45. {
    46. return _task_queue.empty();
    47. }
    48. void handlertask(const string &name)
    49. { // 每个线程都执行这个方法
    50. while (true)
    51. {
    52. // 拿任务
    53. lockqueue();
    54. while (isempty() && _isrunning) // 没任务并且线程不退出
    55. { // 防止伪唤醒·用while
    56. // 为空的任务列表,那就去休眠
    57. _sleep_thread_num++;
    58. Sleep();
    59. _sleep_thread_num--;
    60. }
    61. // 判定一种情况
    62. if (isempty() && !_isrunning)
    63. { // 任务队列空的并且线程池想退出
    64. cout << name << ": " << " quit" << endl ;
    65. unlockqueue();
    66. break;
    67. }
    68. // 有任务
    69. T t = _task_queue.front(); // 取任务
    70. _task_queue.pop(); // 老任务弹走
    71. unlockqueue();
    72. t(); // 处理任务,此处不用/不能在临界区中处理,避免浪费时间因为任务已经是你的了,你自己去处理,不占用公共资源
    73. cout << name << ": " << t.result() << endl;
    74. }
    75. }
    76. public:
    77. threadpool(int thread_num = faultnum) : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0)
    78. {
    79. pthread_mutex_init(&_mutex, nullptr);
    80. pthread_cond_init(&_cond, nullptr);
    81. }
    82. void init()
    83. { // 初始化
    84. func_t func = bind(&threadpool::handlertask, this, placeholders::_1);
    85. for (int i = 0; i < _thread_num; i++)
    86. {
    87. string threadname = "thread-" + to_string(i + 1);
    88. _threads.emplace_back(threadname, func); // 提供名字和任务
    89. // emplace_back它的作用是在容器的末尾直接构造一个对象。emplace_back允许你直接传递构造对象所需的参数
    90. }
    91. }
    92. void start()
    93. { // 开始
    94. _isrunning = true;
    95. for (auto &threadd : _threads)
    96. {
    97. threadd.start();
    98. }
    99. }
    100. void equeue(const T &in)
    101. { // 向线程池中推送任务
    102. lockqueue(); //
    103. if (_isrunning)//运行才可以
    104. {
    105. _task_queue.push(in);
    106. if (_sleep_thread_num > 0)
    107. wakeup(); // 唤醒
    108. }
    109. unlockqueue(); //
    110. }
    111. void stop()
    112. {
    113. lockqueue();
    114. _isrunning = false;
    115. wakeupall();
    116. unlockqueue();
    117. }
    118. ~threadpool()
    119. {
    120. pthread_mutex_destroy(&_mutex);
    121. pthread_cond_destroy(&_cond);
    122. }
    123. private:
    124. int _thread_num; // 期待有多少线程
    125. vector<thread> _threads; // 对象全在这里
    126. queue<T> _task_queue; // 任务队列,他就是临界资源
    127. bool _isrunning; // 是否运行
    128. int _sleep_thread_num; // 休眠的线程的个数
    129. pthread_mutex_t _mutex; // 对queue的锁
    130. pthread_cond_t _cond; // 条件变量
    131. };

    日志:软件运行的记录信息,向显示器打印,向文件打印,特定的格式(统一格式输出) ;

    格式:[日志等级][pid][filename][filenumber][time] 日志内容(支持可变参数);

    日志等级:

    1. DEBUG

    //详细的信息,用于调试程序。包括内部状态和控制流的信息。通常在开发和测试阶段使用。

    2. INFO

    //一般信息,表示程序正常运行时的重要事件。例如系统启动、关闭或者某个操作成功完成等。

    3. WARNING

    //警告信息,表示可能会导致问题的事件。并不意味着错误,但可能需要注意的情况,例如某个功能即将过时。

    4. ERROR

    //错误信息,表示程序中发生了问题。这些问题会影响某个特定的功能或操作,但不会导致程序崩溃。

    5. FATAL  --致命的 

    //致命错误,表示程序遇到严重问题并即将停止运行。例如,关键资源不可用,无法继续执行。

  • 相关阅读:
    linux中断下文工作队列之共享工作队列(中断四)
    python环境迁移:从联网笔记本到离线服务器
    Python的PyQt框架的使用-构建环境篇
    【tensorflow2.6】图片数据建模流程:猫狗分类,83.6%识别率
    弘辽科技:拼多多品质竞价怎么报名?要遵循什么规则?
    Vite:轻量级的前端构建工具
    java版小程序商城免费搭建-直播商城平台规划及常见的营销模式有哪些?电商源码/小程序/三级分销
    企业如何突破瓶颈期的产品营销困境——全民拼购,不伤人脉的营销
    第2章 C语言高级的函数
    AI 绘画基础 - 细数 Stable Diffusion 中的各种常用模型 【🧙 魔导士装备图鉴】
  • 原文地址:https://blog.csdn.net/yiqizhuashuimub/article/details/143392235