?前言:在当今这个信息化高速发展的时代,多线程并发编程已经成为开发高性能应用不可或缺的技能。而在linux这一广泛应用的操作系统中,线程池作为一种高效管理线程资源的机制,更是成为了众多开发者关注的焦点
希望本文能够成为大家学习Linux线程池路上的得力助手,助力大家在多线程并发编程的道路上越走越远。
?1. 日志日志记录了系统运行的各种信息,对于排查问题和维护系统安全非常重要。通过备份和归档日志文件,可以在需要的时候快速地查找和分析问题。同时,定期清理日志文件可以释放磁盘空间,保留必要的日志信息用于后续的分析和排查问题
代码示例:(日志 Log.hpp)
代码语言:javascript代码运行次数:0运行复制#pragma once#include <iostream>#include <unistd.h>#include <pthread.h>#include <string>#include <ctime>#include <stdarg.h>#include <sys/types.h>#include <fcntl.h>#include <sys/stat.h>using namespace std;// 日志的信息enum{ Debug = 0, Info, Warning, Error, Fatal};// 日志文件输出方式enum{ Screen = 10, OneFile, ClassFile};string LevelToString(int level){ switch(level) { case Debug: return "Debug"; case Info: return "Info"; case Warning: return "Warning"; case Error: return "Error"; case Fatal: return "Fatal"; default: return "Unkonw"; }}const int defaultstyle = Screen;const string default_filename = "log.";const string logdir = "log";class Log{public: Log() :style(defaultstyle) ,filename(default_filename) { // 建立专门存放日志文件的目录 mkdir(logdir.c_str(), 0775); } void Enable(int sty) { style = sty; } // 日志的时间格式 string TimeStampExLocalTime() { time_t currtime = time(nullptr); struct tm *curr = localtime(&currtime); char time_buffer[1024]; snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d_%d:%d:%d",\ curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday, curr->tm_hour,\ curr->tm_min, curr->tm_sec); return time_buffer; }// 写入一个日志文件 void WriteLogToOneFile(string &logname, string &message) { umask(0); int fd = open(logname.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0664); if(fd < 0) return; write(fd, message.c_str(), sizeof(message)); close(fd); }// 写入一类日志文件 void WriteLogToClassFile(const string &levelstr, string &message) { // 分级建立目录 string logname = logdir; logname += "/"; logname += filename; logname += levelstr;// 转化成写入一个日志文件 WriteLogToOneFile(logname, message); }// 将信息写入文件 void WriteLog(string &levelstr, string &message) { // 根据style选择写入方式 switch(style) { case Screen: cout << message << endl; break; case OneFile: WriteLogToClassFile("all", message); break; case ClassFile: WriteLogToClassFile(levelstr, message); break; default: cout << "Unknow" << endl; break; } }// 打印日志信息 void LogMessage(int level, const char *format, ...) // 日志接口 { char right_buffer[1024]; va_list args; va_start(args, format); // args 指向了可变参数部分 vsnprintf(right_buffer, sizeof(right_buffer), format, args); va_end(args); char left_buffer[1024]; string currtime = TimeStampExLocalTime(); // 时间 string levelstr = LevelToString(level); // 等级 string idstr = to_string(getpid()); // id snprintf(left_buffer, sizeof(left_buffer), "[%s][%s][%s]", \ levelstr.c_str(), currtime.c_str(), idstr.c_str()); // printf("%s: %s\n", left_buffer, right_buffer); string loginfo = left_buffer; loginfo += right_buffer; WriteLog(levelstr, loginfo); } ~Log() {}private: int style; // 日志的输出风格 string filename; // 文件名称};
实现功能:
代码语言:javascript代码运行次数:0运行复制#include "Log.hpp" using namespace std;int main(){ Log lg; // lg.Enable(ClassFile); lg.Enable(Screen); lg.LogMessage(Debug, "%s:%d-%.2f", "爱你", 1314, 5.20); lg.LogMessage(Info, "%s:%d-%.2f", "爱你", 1314, 5.20); lg.LogMessage(Warning, "%s:%d-%.2f", "爱你", 1314, 5.20); lg.LogMessage(Error, "%s:%d-%.2f", "爱你", 1314, 5.20); lg.LogMessage(Fatal, "%s:%d-%.2f", "爱你", 1314, 5.20); lg.LogMessage(Info, "%s:%d-%.2f", "爱你", 1314, 5.20);return 0;}
模拟实现日志代码
这些线程在执行完一个任务后,并不会被销毁,而是会继续等待并执行新的任务。线程池通过重用线程,减少了线程创建和销毁的开销,从而提高了程序的执行效率
线程池的应用场景:
需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了对性能要求苛刻的应用,比如要求服务器迅速响应客户请求接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误代码示例:(线程池 ThreadPool.hpp)
代码语言:javascript代码运行次数:0运行复制#pragma once#include <iostream>#include <unistd.h>#include <pthread.h>#include <string>#include <functional>#include <ctime>#include <queue>#include <stdarg.h>#include <sys/types.h>#include "Task.hpp"using namespace std;class ThreadData{public: ThreadData(const string name) :threadname(name) {} ~ThreadData() {}public: string threadname;};const int default_value = 5; // 多线程的最大数量template <class T>class ThreadPool{// 单例模式private: ThreadPool(int thread_num = default_value) :_thread_num(thread_num) { pthread_mutex_init(&_mutex, nullptr); pthread_cond_init(&_cond, nullptr); for(int i = 0; i < _thread_num; i++) { string threadname = "thread-"; threadname += to_string(i+1); ThreadData td(threadname); // Thread<ThreadData> t(threadname, bind(&ThreadPool<T>::ThreadRun, this, placeholders::_1), td); _threads.emplace_back(threadname, bind(&ThreadPool<T>::ThreadRun, this, placeholders::_1), td); } } ThreadPool(const ThreadPool<T> &tp) = delete; const ThreadPool<T> &operator=(const ThreadPool<T>) = delete;public: static ThreadPool<T> *GetInstance() { if(instance == nullptr) { LockGuard lockguard(&sig_lock); if(instance == nullptr) { lg.LogMessage(Info, "创建单例成功 ...\n"); instance = new ThreadPool<T>(); } } return instance; } ThreadPool(int thread_num = default_value) :_thread_num(thread_num) { pthread_mutex_init(&_mutex, nullptr); pthread_cond_init(&_cond, nullptr); for(int i = 0; i < _thread_num; i++) { string threadname = "thread-"; threadname += to_string(i+1); ThreadData td(threadname); // Thread<ThreadData> t(threadname, bind(&ThreadPool<T>::ThreadRun, this, placeholders::_1), td); _threads.emplace_back(threadname, bind(&ThreadPool<T>::ThreadRun, this, placeholders::_1), td); } } bool Start() { for(auto &thread : _threads) { thread.Start(); lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str()); } return true; } void Threadwait(const ThreadData &td) { lg.LogMessage(Debug, "no task, %s is sleeping ...\n", td.threadname.c_str()); pthread_cond_wait(&_cond, &_mutex); } void ThreadWakeup() { pthread_cond_signal(&_cond); } void ThreadRun(ThreadData &td) { while(true) { // 取任务 T t; { pthread_mutex_lock(&_mutex); while(_q.empty()) { Threadwait(td); lg.LogMessage(Debug, "have task, %s is wakeup ...\n", td.threadname.c_str()); } t = _q.front(); _q.pop(); pthread_mutex_unlock(&_mutex); sleep(1); } // 处理任务 t.Run(); lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",td.threadname.c_str(), t.PrintTask().c_str(), t.PrintResult().c_str()); } } void Push(T &in) { lg.LogMessage(Debug, "other thread push a task is : %s\n", in.PrintTask().c_str()); LockGuard lockguard(&_mutex); _q.push(in); ThreadWakeup(); } ~ThreadPool() { pthread_mutex_destroy(&_mutex); pthread_cond_destroy(&_cond); } // for test void Wait() { for(auto &thread : _threads) { thread.Join(); } }private: queue<T> _q; vector<Thread<ThreadData>> _threads; int _thread_num; pthread_mutex_t _mutex; pthread_cond_t _cond;// 单例模式 static ThreadPool<T> *instance; static pthread_mutex_t sig_lock;};// 单例模式template<class T>ThreadPool<T> *ThreadPool<T>::instance = nullptr;template<class T>pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;
实现功能:
代码语言:javascript代码运行次数:0运行复制#include "LockGuard.hpp"#include "Log.hpp"#include "Thread.hpp"#include "Task.hpp"#include "ThreadPool.hpp"#include <memory>using namespace std;int main(){ sleep(10); ThreadPool<Task>::GetInstance()->Start(); srand((uint64_t)time(nullptr)); while(true) { int x = rand() % 100 + 1; usleep(rand() % 123); int y = rand() % 200; // int y = 0; usleep(rand() % 123); char oper = opers[rand() % opers.size()]; Task t(x, y, oper); t.Enable(ok); ThreadPool<Task>::GetInstance()->Push(t); sleep(1); } ThreadPool<Task>::GetInstance()->Wait(); return 0;}
模拟实现线程池代码
读写锁:
注意:写独占,读共享,读锁优先级高
核心规则:
写-写互斥:不能有两个写者同时进行写操作读-写互斥:不能同时有一个线程在读,而另一个线程在写读-读允许:可以有一个或多个读者在读读者写者问题和生产者消费者模型大差不差,同样满足我们之前说的321模型
读写锁接口:
初始化
代码语言:javascript代码运行次数:0运行复制int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
销毁
代码语言:javascript代码运行次数:0运行复制int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加锁和解锁
代码语言:javascript代码运行次数:0运行复制int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 读者加锁int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 写者加锁int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 解锁
随着本文的逐渐收尾,我们对Linux线程池的学习之旅也即将告一段落。但请记住,这仅仅是一个开始,而非终点。线程池作为并发编程中的核心组件,其深度和广度远超我们在这篇简短文章中所能涵盖的范围
愿你在未来的学习和工作中,能够充分利用线程池技术,打造出更加高效、稳定的并发应用!
希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行! 谢谢大家支持本篇到这里就结束了,祝大家天天开心!
以上就是【Linux线程】从零到一:掌握Linux线程池的设计与实现的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号