当前位置: 首页 > news >正文

子非线程池中物

线程池,又好上了

有任务队列

任务要处理就直接放到里面

预先创建好线程,本质上也是一个生产消费模型

线程池真是麻烦啊

我们可以直接沿用之前写过的代码,Thread.hpp:

#pragma once
#include <iostream>
#include <functional>
#include <string>
#include <pthread.h>namespace ThreadMoudle
{class ThreadData{public:ThreadData(const std::string &name, pthread_mutex_t *lock) : _name(name), _lock(lock){}public: // 正常来说应该是私有但是我不想写接口了凑合看吧std::string _name;pthread_mutex_t *_lock;};// 线程要执行的方法using func_t = std::function<void()>;       //返回值是void,参数是空//typedef void (*func_t)(ThreadData *td); // 函数指针类型class Thread{public:void Excute(){_isrunning = true;_func();_isrunning = false;}public:Thread(const std::string &name, func_t func) : _name(name), _func(func){std::cout<<"create"<<name<<"done"<<std::endl;}static void *ThreadRoutine(void *args) // 新线程执行的方法{Thread *self = static_cast<Thread *>(args); // 获得当前对象self->Excute();return nullptr;}bool Start(){int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this); // 这个::指用系统提供的if (n != 0){return false;}return true;}std::string Status() // 线程启动检测下状态{if (_isrunning){return "running";}return "sleep";}void Stop(){if (_isrunning){::pthread_cancel(_tid);_isrunning = false;}}void Join(){::pthread_join(_tid, nullptr);std::cout << "join done" << std::endl;}std::string Name(){return _name;}~Thread(){Stop();Join();}private:std::string _name;pthread_t _tid;bool _isrunning;func_t _func; // 线程要执行的回调函数//ThreadData *_td;// std::string _result;        //返回值,不关心的话也可以不用写};
}

这是新写的热乎线程池:

#pragma once#include<iostream>
#include<unistd.h>
#include<string>
#include<vector>
#include<queue>
#include"Thread.hpp"using namespace ThreadMoudle;static const int gdefaultnum = 5;void test()
{while (true){std::cout << "hello EPI?" << std::endl;sleep(1); }
}template<typename T>
class ThreadPool
{
public:ThreadPool(int thraed_num = gdefaultnum):_thread_num(thraed_num),_isrunning(false){}void Init(){for(int i=0;i<_thread_num;i++){std::string threadname = "thread-" + std::to_string(i+1);_threads.emplace_back(threadname,test);}}void Start(){for(auto &thread:_threads){thread.Start();}}void Stop(){}void Enqueue(const T &in){}~ThreadPool(){}
private:int _thread_num;std::vector<Thread> _threads;std::queue<T> _task_queue;bool _isrunning;
};

 Makefile:

threadpool:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f threadpool

Main.cc:

#include"ThreadPool.hpp"int main()
{//std::unique_ptr<ThreadPool> tp = std::make_unique<ThreadPool>();ThreadPool<int> *tp = new ThreadPool<int>();tp->Init();tp->Start();while (true){//不断向线程池中推送任务sleep(1);}return 0;
}

 这是成就:

我们的队列是一个临界资源,我们要对它进行保护捏

这个_task_queue队列是要被主线程访问的

为什么用锁不用信号量呢?

因为都是整体使用,会使用这里的各种属性,我们无法确定里面什么样子

这个线程池的设计思想就是

有任务就去执行

如果没任务就该休眠勒

终于可以正常运行勒,,,

遇到的bug:线程休眠的个数初始设置成0了导致检测的时候检测不到都没有被唤醒

就像是早八的时候一个宿舍都在睡觉

任务还是用之前的代码,唤醒的时候要用while以避免伪唤醒的情况

在初始化中用bind让一个模块能调用另一个模块的方法,,捏

ThreadPool.hpp:

#pragma once#include<iostream>
#include<unistd.h>
#include<functional>
#include<string>
#include<vector>
#include<pthread.h>
#include<queue>
#include"Thread.hpp"using namespace ThreadMoudle;static const int gdefaultnum = 5;void test()
{while (true){std::cout << "hello EPI?" << std::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 Sleep(){pthread_cond_wait(&_cond,&_mutex);}bool IsEmpty(){return _task_queue.empty();}void HandlerTask(){while (true){LockQueue();while(IsEmpty())        //为了防止伪唤醒的情况发生{_sleep_thread_num++;    //保证加锁和解锁都安全更新Sleep();_sleep_thread_num--;}//有任务T t = _task_queue.front();  //取出_task_queue.pop();  //老的弹出去UnLockQueue();t();    //处理任务,不能在临界区处理std::cout << t.debug() << std::endl;}}
public:ThreadPool(int thraed_num = gdefaultnum):_thread_num(thraed_num),_isrunning(false),_sleep_thread_num(0){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_cond,nullptr);}void Init(){func_t func = std::bind(&ThreadPool::HandlerTask,this);     //让this和handlertask强关联起来,能让一个模块调用另一个类中的方法for(int i=0;i<_thread_num;i++){std::string threadname = "thread-" + std::to_string(i+1);_threads.emplace_back(threadname,func);}}void Start(){for(auto &thread:_threads){thread.Start();}}void Stop(){}void Enqueue(const T &in){LockQueue();        //加锁_task_queue.push(in);if(_sleep_thread_num > 0){WakeUp();}UnLockQueue();      //解锁}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}
private:int _thread_num;std::vector<Thread> _threads;std::queue<T> _task_queue;bool _isrunning;int _sleep_thread_num;      //我们定义一个计数器来确定什么时候唤醒它pthread_mutex_t _mutex;pthread_cond_t _cond;
};

Thread.hpp:

#pragma once
#include <iostream>
#include <functional>
#include <string>
#include <pthread.h>namespace ThreadMoudle
{class ThreadData{public:ThreadData(const std::string &name, pthread_mutex_t *lock) : _name(name), _lock(lock){}public: // 正常来说应该是私有但是我不想写接口了凑合看吧std::string _name;pthread_mutex_t *_lock;};// 线程要执行的方法using func_t = std::function<void()>;       //返回值是void,参数是空//typedef void (*func_t)(ThreadData *td); // 函数指针类型class Thread{public:void Excute(){_isrunning = true;_func();_isrunning = false;}public:Thread(const std::string &name, func_t func) : _name(name), _func(func){std::cout<<"create"<<name<<"done"<<std::endl;}static void *ThreadRoutine(void *args) // 新线程执行的方法{Thread *self = static_cast<Thread *>(args); // 获得当前对象self->Excute();return nullptr;}bool Start(){int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this); // 这个::指用系统提供的if (n != 0){return false;}return true;}std::string Status() // 线程启动检测下状态{if (_isrunning){return "running";}return "sleep";}void Stop(){if (_isrunning){::pthread_cancel(_tid);_isrunning = false;}}void Join(){::pthread_join(_tid, nullptr);std::cout << "join done" << std::endl;}std::string Name(){return _name;}~Thread(){Stop();Join();}private:std::string _name;pthread_t _tid;bool _isrunning;func_t _func; // 线程要执行的回调函数//ThreadData *_td;// std::string _result;        //返回值,不关心的话也可以不用写};
}

 Main.cc:

#include"ThreadPool.hpp"
#include"Task.hpp"int main()
{//std::unique_ptr<ThreadPool> tp = std::make_unique<ThreadPool>();ThreadPool<Task> *tp = new ThreadPool<Task>();tp->Init();tp->Start();while (true){//不断向线程池中推送任务sleep(1);Task t(1,1);tp->Enqueue(t);sleep(1);}return 0;
}

Makefile:

threadpool:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f threadpool

Task.hpp:

#pragma once#include <iostream>
#include <string>class Task 
{
public:Task() : x(0), y(0) {}Task(int a, int b) : x(a), y(b) {}// 执行任务,计算结果void operator()() {result_value = x + y; // 示例操作:计算 x 和 y 的和}// 返回结果int result() const {return result_value;}// 调试输出std::string debug() const {return "Task: x = " + std::to_string(x) + ", y = " + std::to_string(y);}private:int x; // 第一个参数int y; // 第二个参数int result_value; // 计算结果
};

 荷叶饭说如果把Makefile都交上去了那证明没什么可交勒

关于线程池退出 方面需要先厘清逻辑,就是如果它任务队列为空而且要退出那就退出捏:

#pragma once#include <iostream>
#include <unistd.h>
#include <functional>
#include <string>
#include <vector>
#include <pthread.h>
#include <queue>
#include "Thread.hpp"using namespace ThreadMoudle;static const int gdefaultnum = 5;void test()
{while (true){std::cout << "hello EPI?" << std::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(){pthraed_cond_broadcast(&_cond);}void Sleep(){pthread_cond_wait(&_cond, &_mutex);}bool IsEmpty(){return _task_queue.empty();}void HandlerTask(){while (true){LockQueue();while (IsEmpty() && _isrunning) // 为了防止伪唤醒的情况发生{_sleep_thread_num++; // 保证加锁和解锁都安全更新Sleep();_sleep_thread_num--;}// 判定一种情况if (IsEmpty() && !_isrunning) // 空了并且退出那就退罢{std::cout << name << "quit" << std::endl;UnLockQueue();break;}// 有任务T t = _task_queue.front(); // 取出_task_queue.pop();         // 老的弹出去UnLockQueue();t(); // 处理任务,不能在临界区处理std::cout << t.debug() << std::endl;}}public:ThreadPool(int thraed_num = gdefaultnum) : _thread_num(thraed_num), _isrunning(false), _sleep_thread_num(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}void Init(){func_t func = std::bind(&ThreadPool::HandlerTask, this); // 让this和handlertask强关联起来,能让一个模块调用另一个类中的方法for (int i = 0; i < _thread_num; i++){std::string threadname = "thread-" + std::to_string(i + 1);_threads.emplace_back(threadname, func);}}void Start(){_isrunning = true;for (auto &thread : _threads){thread.Start();}}void Stop(){LockQueue();_isrunning = false;WakeUpAll();UnLockQueue();}void Enqueue(const T &in){LockQueue(); // 加锁if (_isrunning){_task_queue.push(in);if (_sleep_thread_num > 0){WakeUp();}}UnLockQueue(); // 解锁}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}private:int _thread_num;std::vector<Thread> _threads;std::queue<T> _task_queue;bool _isrunning;int _sleep_thread_num; // 我们定义一个计数器来确定什么时候唤醒它pthread_mutex_t _mutex;pthread_cond_t _cond;
};

 总结

 线程池: 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。

线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

线程池的应用场景:

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

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

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

线程池示例:

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

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

线程安全的单例模式

劳斯,什么是单例模式?

单例模式是一种经典的常用的常考的设计模式

什么是设计模式?(有一种递归的感觉由于什么都不知道导致需要连续的搜索一个概念中的另一个概念)

IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是设计模式,,,球球你们不要再跟我比了,计算机比肩土木就都老实勒

《未毕业勿扰》

单例模式的特点:

某些类, 只应该具有一个对象(实例), 就称之为单例,例如一个男人只能有一个媳妇,在很多服务器开发场景中,经常需要让服务器加载很多的数据 (上百G) 到内存中

此时往往要用一个单例的类来管理这些数据

饿汉方式和懒汉方式

关于单例模式的实现,有饿汉方式和懒汉方式

拿洗碗来举例子:

吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭 

吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式

懒汉方式最核心的思想是 "延时加载". 从而能够优化服务器的启动速度

这是饿汉方式:

template <typename T> 
class Singleton 
{ static T data; 
public: static T* GetInstance() { return &data; } 
}; 

通过Single这个包装类来使用T对象,则一个进程只有一个T对象的实例

 这是懒汉方式:

template <typename T> 
class Singleton 
{ static T* inst; 
public: static T* GetInstance() { if (inst == NULL) { inst = new T(); } return inst; } 
}; 

但是这个实现会造成线程不安全的问题

在第一次嗲用GetInstance的时候,如果两个线程同时调用,可能会创建出两份T对象的实例,但后续再次调用就没问题勒 

日志是软件运行的记录信息,可以向显示器打印,也可以向文件中打印,这个要有特定的格式

[日志等级][pid][filename][filenumber]日志内容(支持可变参数)

 日志等级分为debug(调试信息)和info(常规信息)和warning(警告)和error(错误)和fatal(致命错误)

我们来写一个日志罢,方便看到输出

耶,Log.hpp:

#pragma#include <iostream>
#include<unistd.h>
#include<sys/types.h>
#include<ctime>enum
{DEBUG = 1,INFO,WARNING,ERRNO,FATAL
};std::string LevelToString(int level)
{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERRNO:return "ERRNO";case FATAL:return "FATAL";default:return "UNKNOW";}
}std::string GetCurTime()
{time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%02d-%02d : %02d-%02d-%02d",\curr_time->tm_year+1900,\curr_time->tm_mon+1,\curr_time->tm_mday,\curr_time->tm_hour,\curr_time->tm_min,\curr_time->tm_sec);return buffer;
}class logmessage
{
public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;
};
class Log
{
public:Log(){}void LogMessage(std::string filename,int filenumber,int level,const char* format,...)  //介素可变参数{logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber =filenumber;lg._curr_time = GetCurTime();}~Log(){}
private:
};// int main()
// {//     return 0;
// }

可变参数是从右到左入栈的

#pragma#include <iostream>
#include<unistd.h>
#include<sys/types.h>
#include<ctime>
#include<stdarg.h>enum
{DEBUG = 1,INFO,WARNING,ERRNO,FATAL
};std::string LevelToString(int level)
{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERRNO:return "ERRNO";case FATAL:return "FATAL";default:return "UNKNOW";}
}std::string GetCurTime()
{time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%02d-%02d : %02d-%02d-%02d",\curr_time->tm_year+1900,\curr_time->tm_mon+1,\curr_time->tm_mday,\curr_time->tm_hour,\curr_time->tm_min,\curr_time->tm_sec);return buffer;
}class logmessage
{
public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;
};
class Log
{
public:Log(){}void LogMessage(std::string filename,int filenumber,int level,const char* format,...)  //介素可变参数{logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber =filenumber;lg._curr_time = GetCurTime();va_list ap;va_start(ap,format);    //初始化char log_info[1024];vsnprintf(log_info,sizeof(log_info),format,ap);va_end(ap);lg._message_info = log_info;std::cout << lg._message_info << std::endl;}~Log(){}
private:
};// int main()
// {//     return 0;
// }
#include"ThreadPool.hpp"
#include"Task.hpp"
#include"Log.hpp"int main()
{std::cout << GetCurTime() << std::endl;Log lg;lg.LogMessage("main.cc",10,DEBUG,"hello %d,world: %c,hello: %f\n",1000,'A',3.14);return 0;
}

我恨你啊

我的茶餐厅,,,

没了全没了

 

为什么呢?

可能因为我喜欢吧。

来看看怎么把消息打到文件里吧:

#pragma#include <iostream>
#include<unistd.h>
#include<sys/types.h>
#include<ctime>
#include<stdarg.h>
#include<fstream>
#include<cstring>#define SCREEN_TYPE 1
#define FILE_TYPE 2const std::string glogfile = "./log.txt";enum
{DEBUG = 1,INFO,WARNING,ERRNO,FATAL
};std::string LevelToString(int level)
{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERRNO:return "ERRNO";case FATAL:return "FATAL";default:return "UNKNOW";}
}std::string GetCurTime()
{time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%02d-%02d : %02d-%02d-%02d",\curr_time->tm_year+1900,\curr_time->tm_mon+1,\curr_time->tm_mday,\curr_time->tm_hour,\curr_time->tm_min,\curr_time->tm_sec);return buffer;
}class logmessage
{
public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;
};
class Log
{
public:Log(const std::string &logfile = glogfile):_logfile(logfile),_type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const logmessage &lg){printf("[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time,lg._message_info);}void FlushLogToFile(const logmessage &lg){std::ofstream out(_logfile);if(!out.is_open()){return;}char logtxt[2048];snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time,lg._message_info);out.write(logtxt,strlen(logtxt));out.close();}void FlushLog(const logmessage &lg){switch(_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void LogMessage(std::string filename,int filenumber,int level,const char* format,...)  //介素可变参数{logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber =filenumber;lg._curr_time = GetCurTime();va_list ap;va_start(ap,format);    //初始化char log_info[1024];vsnprintf(log_info,sizeof(log_info),format,ap);va_end(ap);lg._message_info = log_info;//打印出来日志FlushLog(lg);}~Log(){}
private:int _type;std::string _logfile;
};// int main()
// {//     return 0;
// }
#include"ThreadPool.hpp"
#include"Task.hpp"
#include"Log.hpp"int main()
{std::cout << GetCurTime() << std::endl;Log lg;lg.Enable(FILE_TYPE);lg.LogMessage("main.cc",10,DEBUG,"hello %d,world: %c,hello: %f\n",1000,'A',3.14);return 0;
}

这个打印乱码不知道为什么,好像是说什么强制类型转换出问题了

我也不知道哪错了那就不改了吧

do while(0)是保证宏替换的安全问题

#pragma#include <iostream>
#include<unistd.h>
#include<sys/types.h>
#include<ctime>
#include<stdarg.h>
#include<fstream>
#include<cstring>
#include"LockGuard.hpp"#define SCREEN_TYPE 1
#define FILE_TYPE 2const std::string glogfile = "./log.txt";
pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;enum
{DEBUG = 1,INFO,WARNING,ERRNO,FATAL
};std::string LevelToString(int level)
{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERRNO:return "ERRNO";case FATAL:return "FATAL";default:return "UNKNOW";}
}std::string GetCurTime()
{time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%02d-%02d : %02d-%02d-%02d",\curr_time->tm_year+1900,\curr_time->tm_mon+1,\curr_time->tm_mday,\curr_time->tm_hour,\curr_time->tm_min,\curr_time->tm_sec);return buffer;
}class logmessage
{
public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;
};class Log
{
public:Log(const std::string &logfile = glogfile):_logfile(logfile),_type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const logmessage &lg){printf("[%s][%d][%s][%d][%s][%s]",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time,lg._message_info);}void FlushLogToFile(const logmessage &lg){std::ofstream out(_logfile,std::ios::app);if(!out.is_open()){return;}char logtxt[2048];snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time,lg._message_info);out.write(logtxt,strlen(logtxt));out.close();}void FlushLog(const logmessage &lg){//加过滤逻辑,可以把等级过滤出去LockGuard lockguard(&glock);switch(_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void LogMessage(std::string filename,int filenumber,int level,const char* format,...)  //介素可变参数{logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber =filenumber;lg._curr_time = GetCurTime();va_list ap;va_start(ap,format);    //初始化char log_info[1024];vsnprintf(log_info,sizeof(log_info),format,ap);va_end(ap);lg._message_info = log_info;//打印出来日志FlushLog(lg);}~Log(){}
private:int _type;std::string _logfile;
};Log lg;#define LOG(Level,Format,...)do{lg.LogMessage(__FILE__,__LINE__,Level,Format,##__VA_ARGS__); }while(0)
#define EnableScreen() do {lg.Enable(SCREEN_TYPE);}while(0)
#define EnableFILE() do{lg.Enable(FILE_TYPE;)}while(0)

赫赫恨

亲爱的荷叶饭如果 你看到这里

和我一起改bug,我在这存个档

Log.hpp:

#pragma once#include <iostream>
#include<unistd.h>
#include<sys/types.h>
#include<ctime>
#include<stdarg.h>
#include<fstream>
#include<cstring>
#include"LockGuard.hpp"#define SCREEN_TYPE 1
#define FILE_TYPE 2
//下面这个是改进之后
Log lg;#define LOG(Level, Format, ...) do { \lg.LogMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \
} while(0)
//#define LOG(Level,Format,...) do{lg.LogMessage(__FILE__,__LINE__,Level,Format,##__VA_ARGS__);}while(0)
#define EnableScreen() do {lg.Enable(SCREEN_TYPE);}while(0)
#define EnableFILE() do{lg.Enable(FILE_TYPE);}while(0)const std::string glogfile = "./log.txt";
pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;enum LogLevel
{DEBUG = 1,INFO,WARNING,ERRNO,FATAL
};std::string LevelToString(int level)
{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERRNO:return "ERRNO";case FATAL:return "FATAL";default:return "UNKNOW";}
}std::string GetCurTime()
{time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%02d-%02d : %02d-%02d-%02d",\curr_time->tm_year+1900,\curr_time->tm_mon+1,\curr_time->tm_mday,\curr_time->tm_hour,\curr_time->tm_min,\curr_time->tm_sec);return buffer;
}class logmessage
{
public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;
};class Log
{
public:Log(const std::string &logfile = glogfile):_logfile(logfile),_type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const logmessage &lg){printf("[%s][%d][%s][%d][%s][%s]",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());}void FlushLogToFile(const logmessage &lg){std::ofstream out(_logfile,std::ios::app);if(!out.is_open()){return;}char logtxt[2048];snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());out.write(logtxt,strlen(logtxt));out.close();}void FlushLog(const logmessage &lg){//加过滤逻辑,可以把等级过滤出去LockGuard lockguard(&glock);switch(_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void LogMessage(std::string filename,int filenumber,int level,const char* format,...)  //介素可变参数{logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber =filenumber;lg._curr_time = GetCurTime();va_list ap;va_start(ap,format);    //初始化char log_info[1024];vsnprintf(log_info,sizeof(log_info),format,ap);va_end(ap);lg._message_info = log_info;//打印出来日志FlushLog(lg);}~Log(){}
private:int _type;std::string _logfile;
};

ThreadPool.hpp:

#pragma once#include"Log.hpp"
#include <iostream>
#include <unistd.h>
#include <functional>
#include <string>
#include <vector>
#include <pthread.h>
#include <queue>
#include "Thread.hpp"using namespace ThreadMoudle;static const int gdefaultnum = 5;void test()
{while (true){std::cout << "hello EPI?" << std::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(){while (true){LockQueue();while (IsEmpty() && _isrunning) // 为了防止伪唤醒的情况发生{_sleep_thread_num++; // 保证加锁和解锁都安全更新Sleep();_sleep_thread_num--;}// 判定一种情况if (IsEmpty() && !_isrunning) // 空了并且退出那就退罢{std::cout << "quit" << std::endl;UnLockQueue();break;}// 有任务T t = _task_queue.front(); // 取出_task_queue.pop();         // 老的弹出去UnLockQueue();t(); // 处理任务,不能在临界区处理std::cout << t.debug() << std::endl;}}ThreadPool(int thread_num = gdefaultnum) : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}ThreadPool(const ThreadPool<T> &) = delete;         //赫赫这就是我们单例模式void operator = (const ThreadPool<T> &) = delete;
public:void Init(){func_t func = std::bind(&ThreadPool::HandlerTask, this); // 让this和handlertask强关联起来,能让一个模块调用另一个类中的方法for (int i = 0; i < _thread_num; i++){std::string threadname = "thread-" + std::to_string(i + 1);_threads.emplace_back(threadname, func);}}void Start(){_isrunning = true;for (auto &thread : _threads){thread.Start();}}void Stop(){LockQueue();_isrunning = false;WakeUpAll();UnLockQueue();}static ThreadPool<T> *GetInstance(){if(_tp==nullptr){LOG(INFO,"create threadpool\n");_tp=new ThreadPool();_tp->Init();_tp->Start();}else{LOG(INFO,"get threadpool\n");}return _tp;}void Enqueue(const T &in){LockQueue(); // 加锁if (_isrunning){_task_queue.push(in);if (_sleep_thread_num > 0){WakeUp();}}UnLockQueue(); // 解锁}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}private:int _thread_num;std::vector<Thread> _threads;std::queue<T> _task_queue;bool _isrunning;int _sleep_thread_num; // 我们定义一个计数器来确定什么时候唤醒它pthread_mutex_t _mutex;pthread_cond_t _cond;//单例模式static ThreadPool<T> *_tp;};//静态指针的初始化需要在类外
template<typename T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;

 Main.cc:

#include"ThreadPool.hpp"
#include"Task.hpp"
#include"Log.hpp"int main()
{Log::FlushLogToScreen();int cnt = 10;while (cnt){sleep(1);Task t(1,1);ThreadPool<Task>::GetInstance()->Enqueue(t);LOG(INFO,"enqueue a task,%s\n",t.debug().c_str());sleep(1);cnt--;}ThreadPool<Task>::GetInstance()->Stop();LOG(INFO,"thraed pool stop!\n");return 0;
}

 LockGuard.hpp:

#pragma once
#include<pthread.h>class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}~LockGuard(){pthread_mutex_unlock(_mutex);}private:pthread_mutex_t *_mutex;
};

Thread.hpp:

#pragma once
#include <iostream>
#include <functional>
#include <string>
#include <pthread.h>namespace ThreadMoudle
{class ThreadData{public:ThreadData(const std::string &name, pthread_mutex_t *lock) : _name(name), _lock(lock){}public: // 正常来说应该是私有但是我不想写接口了凑合看吧std::string _name;pthread_mutex_t *_lock;};// 线程要执行的方法using func_t = std::function<void()>;       //返回值是void,参数是空//typedef void (*func_t)(ThreadData *td); // 函数指针类型class Thread{public:void Excute(){_isrunning = true;_func();_isrunning = false;}public:Thread(const std::string &name, func_t func) : _name(name), _func(func){std::cout<<"create"<<name<<"done"<<std::endl;}static void *ThreadRoutine(void *args) // 新线程执行的方法{Thread *self = static_cast<Thread *>(args); // 获得当前对象self->Excute();return nullptr;}bool Start(){int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this); // 这个::指用系统提供的if (n != 0){return false;}return true;}std::string Status() // 线程启动检测下状态{if (_isrunning){return "running";}return "sleep";}void Stop(){if (_isrunning){::pthread_cancel(_tid);_isrunning = false;}}void Join(){::pthread_join(_tid, nullptr);std::cout << "join done" << std::endl;}std::string Name(){return _name;}~Thread(){Stop();Join();}private:std::string _name;pthread_t _tid;bool _isrunning;func_t _func; // 线程要执行的回调函数//ThreadData *_td;// std::string _result;        //返回值,不关心的话也可以不用写};
}

拜托了小蜜蜂君

相关文章:

子非线程池中物

线程池&#xff0c;又好上了 有任务队列 任务要处理就直接放到里面 预先创建好线程&#xff0c;本质上也是一个生产消费模型 线程池真是麻烦啊 我们可以直接沿用之前写过的代码&#xff0c;Thread.hpp: #pragma once #include <iostream> #include <functional&…...

Unraid的cache使用btrfs或zfs?

Unraid的cache使用btrfs或zfs&#xff1f; 背景&#xff1a;由于在unraid中添加了多个docker和虚拟机&#xff0c;因此会一直访问硬盘。然而&#xff0c;单个硬盘实在难以让人放心。在阵列盘中&#xff0c;可以通过添加校验盘进行数据保护&#xff0c;在cache中无法使用xfs格式…...

微服务实战——平台属性

平台属性 中间表复杂业务 /*** 获取分类规格参数&#xff08;模糊查询&#xff09;** param params* param catelogId* param type type"base"时查询基础属性&#xff0c;type"sale"时查询销售属性* return*/ Override public PageUtils listByCatelogId…...

半监督学习与数据增强(论文复现)

半监督学习与数据增强&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 文章目录 半监督学习与数据增强&#xff08;论文复现&#xff09;概述算法原理核心逻辑效果演示使用方式 概述 本文复现论文提出的半监督学习方法&#xff0c;半监督学习&…...

css3-----2D转换、动画

2D 转换&#xff08;transform&#xff09; 转换&#xff08;transform&#xff09;是CSS3中具有颠覆性的特征之一&#xff0c;可以实现元素的位移、旋转、缩放等效果 移动&#xff1a;translate旋转&#xff1a;rotate缩放&#xff1a;scale 二维坐标系 2D 转换之移动 trans…...

SQL进阶技巧:统计各时段观看直播的人数

目录 0 需求描述 1 数据准备 2 问题分析 3 小结 如果觉得本文对你有帮助&#xff0c;那么不妨也可以选择去看看我的博客专栏 &#xff0c;部分内容如下&#xff1a; 数字化建设通关指南 专栏 原价99&#xff0c;现在活动价39.9&#xff0c;十一国庆后将上升至59.9&#…...

Stream流的终结方法

1.Stream流的终结方法 2.forEach 对于forEach方法&#xff0c;用来遍历stream流中的所有数据 package com.njau.d10_my_stream;import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.function.Consumer; import java.util…...

JavaWeb——Vue组件库Element(4/6):案例:基本页面布局(基本框架、页面布局、CSS样式、完善布局、效果展示,含完整代码)

目录 步骤 基本页面布局 基本框架 页面布局 CSS样式 完善布局 效果展示 完整代码 Element 的基本使用方式以及常见的组件已经了解完了&#xff0c;接下来要完成一个案例&#xff0c;通过这个案例让大家知道如何基于 Element 中的各个组件制作一个完整的页面。 案例&am…...

【c++】 模板初阶

泛型编程 写一个交换函数&#xff0c;在学习模板之前&#xff0c;为了匹配不同的参数类型&#xff0c;我们可以利用函数重载来实现。 void Swap(int& a, int& b) {int c a;a b;b c; } void Swap(char& a, char& b) {char c a;a b;b c; } void Swap(dou…...

R 语言 data.table 大规模数据处理利器

前言 最近从一个 python 下的 anndata 中提取一个特殊处理过的单细胞矩阵&#xff0c;想读入R用来画图&#xff08;个人比较喜欢用R可视化 &#xff09;&#xff0c;保存之后&#xff0c;大概几个G的CSV文件&#xff0c;如果常规方法读入R&#xff0c;花费的时间比较久&#x…...

Java 静态代理详解:为什么代理类和被代理类要实现同一个接口?

在 Java 开发中&#xff0c;代理模式是一种常用的设计模式&#xff0c;其中代理类的作用是控制对其他对象的访问。代理模式分为静态代理和动态代理&#xff0c;在静态代理中&#xff0c;代理类和被代理类都需要实现同一个接口。这一机制为实现透明的代理行为提供了基础&#xf…...

OpenCV C++霍夫圆查找

OpenCV 中的霍夫圆检测基于 霍夫变换 (Hough Transform)&#xff0c;它是一种从边缘图像中识别几何形状的算法。霍夫圆检测是专门用于检测图像中的圆形形状的。它通过将图像中的每个像素映射到可能的圆参数空间&#xff0c;来确定哪些像素符合圆形状。 1. 霍夫变换的原理 霍夫…...

H.264编解码介绍

一、简介 H.264,又称为AVC(Advanced Video Coding),是一种广泛使用的视频压缩标准。它由国际电信联盟(ITU)和国际标准化组织(ISO)联合开发,并于2003年发布。 H.264的发展历史可以追溯到上个世纪90年代。当时,视频压缩技术的主要标准是MPEG-2,但它在压缩率和视频质…...

Java | Leetcode Java题解之第450题删除二叉搜索树中的节点

题目&#xff1a; 题解&#xff1a; class Solution {public TreeNode deleteNode(TreeNode root, int key) {TreeNode cur root, curParent null;while (cur ! null && cur.val ! key) {curParent cur;if (cur.val > key) {cur cur.left;} else {cur cur.rig…...

【CViT】Deepfake Video Detection Using Convolutional Vision Transformer

文章目录 Deepfake Video Detection Using Convolutional Vision Transformerkey points**卷积视觉变压器**FLViT实验总结Deepfake Video Detection Using Convolutional Vision Transformer 会议/期刊:2021 作者: key points 提出了一种用于检测深度伪造的卷积视觉变压器…...

安卓主板_MTK4G/5G音视频记录仪整机及方案定制

音视频记录仪方案&#xff0c;采用联发科MT6877平台八核2* A78 6* A55主频高达2.4GHz, 具有高能低耗特性&#xff0c;搭载Android 12.0智能操作系统&#xff0c;可选4GB32GB/6GB128GB内存&#xff0c;运行流畅。主板集成NFC、双摄像头、防抖以及多种无线数据连接&#xff0c;支…...

Qt 教程全集目录公布(方便查阅)

点击上方"蓝字"关注我们 Qt 安装 以下是常见安装方法和软件获取 Qt4Qt5Qt6版本下载(在线和离线)【网址】...

云计算SLA响应时间的matlab模拟与仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 用matlab模拟&#xff0c;一个排队理论。输入一堆包&#xff0c;经过buffer&#xff08;一个或者几个都行&#xff09;传给server&#xff0c;这些包会在buffer里…...

ARTS Week 42

Algorithm 本周的算法题为 2283. 判断一个数的数字计数是否等于数位的值 给你一个下标从 0 开始长度为 n 的字符串 num &#xff0c;它只包含数字。 如果对于 每个 0 < i < n 的下标 i &#xff0c;都满足数位 i 在 num 中出现了 num[i]次&#xff0c;那么请你返回 true …...

10.2学习

1.IOC控制反转 IoC&#xff08;Inverse of Control:控制反转&#xff09;是⼀种设计思想&#xff0c;就是将原本在程序中⼿动创建对象的控制权&#xff0c;交由Spring框架来管理。 IoC 在其他语⾔中也有应⽤&#xff0c;并⾮ Spring 特有。 ​ IoC 容器是 Spring⽤来实现 IoC …...

【数一线性代数】021入门

Index 推荐阅读&#xff1a;https://blog.csdn.net/weixin_60702024/article/details/141729949分析实现总结 推荐阅读&#xff1a;https://blog.csdn.net/weixin_60702024/article/details/141729949 给定二叉树的根节点root&#xff0c;计算其叶节点的个数。 分析实现 类似…...

(k8s)kubernetes中ConfigMap和Secret

转载&#xff1a;ConfigMap 一、ConfigMap介绍 ConfigMap是一种API对象&#xff0c;用来将非机密性的数据保存到键值对中。使用时&#xff0c;Pod可以将其用作环境变量、命令行参数或存储卷中的配置文件。 ConfigMap将你的环境配置信息和容器镜像解耦&#xff0c;便于应用配置…...

stm32四足机器人(标准库)

项目技术要求 PWM波形的学习 参考文章stm32 TIM输出比较(PWM驱动LED呼吸灯&&PWM驱动舵机&&PWM驱动直流电机)_ttl pwm 驱动激光头区别-CSDN博客 舵机的学习 参考文章 stm32 TIM输出比较(PWM驱动LED呼吸灯&&PWM驱动舵机&&PWM驱动直流电机)…...

基于Hive和Hadoop的共享单车分析系统

本项目是一个基于大数据技术的共享单车分析系统&#xff0c;旨在为用户提供全面的单车使用信息和深入的出行行为分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 S…...

基于SSM和vue的机票订购管理系统

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM和vue的机票订购管理系统2拥有两种角色 管理员&#xff1a;用户管理、机票管理、订票管理、公告管理、广告管理、系统管理、添加机票等 用户&#xff1a;登录注册、订票、查看公…...

【rCore OS 开源操作系统】Rust 练习题题解: Enums

【rCore OS 开源操作系统】Rust 练习题题解: Enums 摘要 rCore OS 开源操作系统训练营学习中的代码练习部分。 在此记录下自己学习过程中的产物&#xff0c;以便于日后更有“收获感”。 后续还会继续完成其他章节的练习题题解。 正文 enums1 题目 // enums1.rs // // No hi…...

VPN简述

文章目录 VPNVPN基础VPN类型 VPN VPN隧道安全 VPN基础 背景&#xff1a; 在网络传输中&#xff0c;绝大部分数据内容都是明文传输&#xff0c;存在很多安全隐患&#xff08;窃听、篡改、冒充&#xff09; 总部、分公司、办事处、出差人员、合作单位等需要访问总部网络资源 Vi…...

【Kubernetes】常见面试题汇总(四十九)

目录 110.假设一家公司希望通过采用新技术来优化其工作负载的分配。公司如何有效地实现这种资源分配&#xff1f; 111.考虑一家拼车公司希望通过同时扩展其平台来增加服务器数量。您认为公司将如何处理服务器及其安装&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【…...

常见排序算法以及冒泡排序的基础使用方法

众所周知&#xff0c;冒泡排序是编程中最经典也是最简单的一种排序方法&#xff0c;它是通过重复访问对两个相邻的值进行比较&#xff0c;由于在互换的过程中&#xff0c;最大 (或最小) 的那个值会慢慢的交换到顶部&#xff0c;像汽水一样&#xff0c;故名“冒泡排序”。 let a…...

【网络安全】Cookie与ID未强绑定导致账户接管

未经许可,不得转载。 文章目录 前言正文前言 DigiLocker 是一项在线服务,旨在为公民提供一个安全的数字平台,用于存储和访问重要的文档,如 Aadhaar 卡、PAN 卡和成绩单等。DigiLocker 通过多因素身份验证(MFA)来保护用户账户安全,通常包括 6 位数的安全 PIN 和一次性密…...

网站建设价格明细表和网站预算/淄博网络推广公司哪家好

Java实现链表的思想可以参考 LinkedList的源码 下面实现几点关于单向链表的一些操作&#xff1a; 1 public class LinkList<E> {2 private Node<E> first;3 private Node<E> last;4 private int size;5 6 public LinkList() {7 …...

郑州做网站建设的公司/深圳网站开发技术

&#xff08;以下几种情况是我在使用时所遇情况以及最终顺利解决的方法&#xff0c;主要针对8.0以上版本的mysql哦&#xff09; 问题一&#xff1a;无法启动mysql&#xff08;在终端输入net start mysql&#xff0c;出现无法启动的情况&#xff09; 解决&#xff1a;可能是因为…...

怎么做公司招聘网站/百度推广在哪里能看到

转自&#xff1a;https://blog.csdn.net/u014430366/article/details/77574222?locationNum5&fps1 其他插件推荐&#xff1a; ignore &#xff1a;git 快速排除无需提交到仓库的文件Maven Helper &#xff1a;快速查看 pom.xml 中引用依赖关系 大概也许是闲得无聊才会装…...

河北网站优化/nba最新排名公布

最近在学习撸撸的代码规范和写法&#xff0c;有些心得&#xff0c;准备好好写一写~包括了多渠道打版&#xff08;以前有写过方法&#xff09;&#xff0c;工厂模式&#xff0c;mvp&#xff0c;以及最近刚封装出来的多渠道多版本展示不同页面的manifestPlaceholders的配置方法&a…...

做非法网站的有没有/中国市场营销网

.NetCore框架Surging系列&#xff08;一&#xff09;介绍 .NetCore框架Surging系列&#xff08;二&#xff09;HTTP .NetCore框架Surging系列&#xff08;三&#xff09;HTTP本地路由发现过程 .NetCore框架Surging系列&#xff08;四&#xff09;RPC客户端过程 .NetCore框架Sur…...

一台独立服务器如何做多个网站/长沙建站优化

今天来聊聊跨平台文件传输。我们在工作&#xff0c;学习中经常会遇到多平台文件互传问题&#xff0c;每个人都有自己的巧妙解决办法&#xff0c;小点的文件可能会选择使用 QQ/微信进行传输&#xff0c;大一些的文件可以使用数据线&#xff0c;U 盘&#xff0c;网盘等方式传输&a…...