线程池: * 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着 监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利 用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
线程池的应用场景:
* 1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技 术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个 Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
* 2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
* 3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情 况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限, 出现错误.
线程池的种类:
* 线程池示例:
* 1. 创建固定数量线程池,循环从任务队列中获取任务对象,
* 2. 获取到任务对象后,执行任务对象中的任务接口

代码的大概整体结构:

main.cc
- #include "threadpool.hpp"
- #include "task.hpp"
-
- int main(){
- threadpool<task>* tp=new threadpool<task>();
- //在使用模板类时,始终需要提供具体的类型参数,这样编译器才能正确生成类型安全的代码。如果不加类型参数,将导致编译错误。
- tp->init();
- tp->start();
- int cnt=10;
- while(cnt){
- //向线程池推任务
- sleep(1);
- task t(1,1);
- tp->equeue(t);
- sleep(1);
- cout<<"cnt: "<<cnt--<<endl;
- }
- tp->stop();
- cout<<"stop!!!!!!!"<<endl;
- sleep(10);
- return 0;
- }
task.hpp
- #pragma once
- #include
- #include
- using namespace std;
-
- class task
- {
- public:
- task() {}
- task(int x, int y) : _x(x), _y(y)
- {
- }
- void excute()
- {
- _result = _x + _y;
- }
- void operator()(){
- excute();
- }
- string debug()
- {
- string msg = to_string(_x) + "+" + to_string(_y) + "=?";
- return msg;
- }
- string result()
- {
- string msg = to_string(_x) + "+" + to_string(_y) +"="+ to_string(_result);
- return msg;
- }
- ~task()
- {
- }
-
- private:
- int _x;
- int _y;
- int _result;
- };
thread.hpp
- #pragma once
- #include <iostream>
- #include <string>
- #include <pthread.h>
- #include <functional> //回调方法
-
- using namespace std;
- namespace threadmodel // 构造一个命名空间
- {
- //typedef void (*func_t)(threaddate *td);
- // typedef void (*func_t)(const string &name); // func_t 是一个指向返回类型为 void 且有参数的函数的指针。
- // // func_t 现在可以用作一个函数指针类型的别名,表示任何指向带字符串参数且返回类型为 void 的函数的指针。
- using func_t=function<void(const string&)>;//返回值为void,参数为空的函数类型
- class thread
- {
- public:
- void excute()
- {
- cout << _name << " is running ! " << endl;
- _running = true;
- _func(_name); // 回调不仅回调运行,他还得结束
- _running = false; // 回调完就结束了
- }
-
- public:
- thread(const string &name, func_t func) : _name(name), _func(func)
- {
- cout << "create: " << name << " done! " << endl;
- }
- static void *threadroutine(void *agv) // 只要线程启动,新线程都会启动这个方法
- { // 因为是类内定义的方法,所以会隐含一个this指针参数,加了static就可以,这是因为 static 成员函数不与类的实例相关联,因此它不需要 this 指针。
- thread *self = static_cast<thread *>(agv); // 获得当前对象。因为要调用_func,但_func是动态的,静态函数无法访问所以传this指针访问
- self->excute();
- return nullptr;
- }
- bool start()
- { // 线程启动方法
- int n = ::pthread_create(&_tid, nullptr, threadroutine, this);
- // 使用::pthread_create确保调用的是全局命名空间中的pthread_create函数,避免当前命名空间内可能存在的同名函数的影响。
- // 直接使用 pthread_create 会根据当前命名空间查找,如果找到了同名函数,就会调用那个函数。
- if (n != 0)
- return false;
- return true;
- }
- string status()
- {
- if (_running)
- {
- return "running";
- }
- else
- return "sleep";
- }
- void stop()
- { // 线程停止方法
- if (_running)
- { // 得先有线程才能停止
- _running = false; // 状态停止
- ::pthread_cancel(_tid);
- cout << _name << " stop ! " << endl;
- }
- }
- void join()
- { // 线程等待方法
- if (!_running)
- { // 没有running才值得join
- ::pthread_join(_tid, nullptr);
- cout << _name << " join ! " << endl;
- }
- }
- string threadname()
- {
- return _name;
- }
- ~thread()
- {
- }
-
- private:
- string _name; // 线程的名字
- pthread_t _tid; // 线程的id
- bool _running; // 是否处于工作状态
- func_t _func; // 线程要执行的回调函数
- };
- };
threadpool.hpp
- #pragma once
- #include <iostream>
- #include <unistd.h>
- #include <string>
- #include <vector>
- #include <queue>
- #include <functional>
- #include "thread.hpp"
-
- using namespace std;
- using namespace threadmodel;
-
- static const int faultnum = 5;
-
- void test()
- {
- while (true)
- {
- cout << "hello world" << endl;
- sleep(1);
- }
- }
-
- template <typename T>
- class threadpool
- {
- private:
- void lockqueue()
- {
- pthread_mutex_lock(&_mutex);
- }
- void unlockqueue()
- {
- pthread_mutex_unlock(&_mutex);
- }
- void wakeup()
- {
- pthread_cond_signal(&_cond); // 唤醒一个
- }
- void wakeupall()
- {
- pthread_cond_broadcast(&_cond); // 唤醒全部
- }
- void Sleep()
- {
- pthread_cond_wait(&_cond, &_mutex); // 等待
- }
- bool isempty()
- {
- return _task_queue.empty();
- }
- void handlertask(const string &name)
- { // 每个线程都执行这个方法
- while (true)
- {
- // 拿任务
- lockqueue();
- while (isempty() && _isrunning) // 没任务并且线程不退出
- { // 防止伪唤醒·用while
- // 为空的任务列表,那就去休眠
- _sleep_thread_num++;
- Sleep();
- _sleep_thread_num--;
- }
- // 判定一种情况
- if (isempty() && !_isrunning)
- { // 任务队列空的并且线程池想退出
- cout << name << ": " << " quit" << endl ;
- unlockqueue();
- break;
- }
- // 有任务
- T t = _task_queue.front(); // 取任务
- _task_queue.pop(); // 老任务弹走
- unlockqueue();
-
- t(); // 处理任务,此处不用/不能在临界区中处理,避免浪费时间因为任务已经是你的了,你自己去处理,不占用公共资源
- cout << name << ": " << t.result() << endl;
- }
- }
-
- public:
- threadpool(int thread_num = faultnum) : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0)
- {
- pthread_mutex_init(&_mutex, nullptr);
- pthread_cond_init(&_cond, nullptr);
- }
- void init()
- { // 初始化
- func_t func = bind(&threadpool::handlertask, this, placeholders::_1);
- for (int i = 0; i < _thread_num; i++)
- {
- string threadname = "thread-" + to_string(i + 1);
- _threads.emplace_back(threadname, func); // 提供名字和任务
- // emplace_back它的作用是在容器的末尾直接构造一个对象。emplace_back允许你直接传递构造对象所需的参数
- }
- }
- void start()
- { // 开始
- _isrunning = true;
- for (auto &threadd : _threads)
- {
- threadd.start();
- }
- }
- void equeue(const T &in)
- { // 向线程池中推送任务
- lockqueue(); // 加
- if (_isrunning)//运行才可以
- {
- _task_queue.push(in);
- if (_sleep_thread_num > 0)
- wakeup(); // 唤醒
- }
- unlockqueue(); // 解
- }
- void stop()
- {
- lockqueue();
- _isrunning = false;
- wakeupall();
- unlockqueue();
- }
- ~threadpool()
- {
- pthread_mutex_destroy(&_mutex);
- pthread_cond_destroy(&_cond);
- }
-
- private:
- int _thread_num; // 期待有多少线程
- vector<thread> _threads; // 对象全在这里
- queue<T> _task_queue; // 任务队列,他就是临界资源
- bool _isrunning; // 是否运行
-
- int _sleep_thread_num; // 休眠的线程的个数
-
- pthread_mutex_t _mutex; // 对queue的锁
- pthread_cond_t _cond; // 条件变量
- };

日志:软件运行的记录信息,向显示器打印,向文件打印,特定的格式(统一格式输出) ;
格式:[日志等级][pid][filename][filenumber][time] 日志内容(支持可变参数);
日志等级:
1. DEBUG
//详细的信息,用于调试程序。包括内部状态和控制流的信息。通常在开发和测试阶段使用。
2. INFO
//一般信息,表示程序正常运行时的重要事件。例如系统启动、关闭或者某个操作成功完成等。
3. WARNING
//警告信息,表示可能会导致问题的事件。并不意味着错误,但可能需要注意的情况,例如某个功能即将过时。
4. ERROR
//错误信息,表示程序中发生了问题。这些问题会影响某个特定的功能或操作,但不会导致程序崩溃。
5. FATAL --致命的
//致命错误,表示程序遇到严重问题并即将停止运行。例如,关键资源不可用,无法继续执行。