【Linux线程】从零到一:掌握Linux线程池的设计与实现

发布时间 - 2025-06-25 00:00:00    点击率:

?前言:在当今这个信息化高速发展的时代,多线程并发编程已经成为开发高性能应用不可或缺的技能。而在linux这一广泛应用的操作系统中,线程池作为一种高效管理线程资源的机制,更是成为了众多开发者关注的焦点

希望本文能够成为大家学习Linux线程池路上的得力助手,助力大家在多线程并发编程的道路上越走越远。

?1. 日志

日志记录了系统运行的各种信息,对于排查问题和维护系统安全非常重要。通过备份和归档日志文件,可以在需要的时候快速地查找和分析问题。同时,定期清理日志文件可以释放磁盘空间,保留必要的日志信息用于后续的分析和排查问题

代码示例:(日志 Log.hpp)

代码语言:javascript代码运行次数:0运行复制
#pragma once#include #include #include #include #include #include #include #include #include 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;}
在这里插入图片描述
在这里插入图片描述

模拟实现日志代码


?2. 线程池

这些线程在执行完一个任务后,并不会被销毁,而是会继续等待并执行新的任务。线程池通过重用线程,减少了线程创建和销毁的开销,从而提高了程序的执行效率


线程池的应用场景:

需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了对性能要求苛刻的应用,比如要求服务器迅速响应客户请求接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误

代码示例:(线程池 ThreadPool.hpp)

代码语言:javascript代码运行次数:0运行复制
#pragma once#include #include #include #include #include #include #include #include #include #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 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 t(threadname, bind(&ThreadPool::ThreadRun, this, placeholders::_1), td);                        _threads.emplace_back(threadname, bind(&ThreadPool::ThreadRun, this, placeholders::_1), td);        }    }    ThreadPool(const ThreadPool &tp) = delete;    const ThreadPool &operator=(const ThreadPool) = delete;public:    static ThreadPool *GetInstance()    {        if(instance == nullptr)        {            LockGuard lockguard(&sig_lock);            if(instance == nullptr)            {                lg.LogMessage(Info, "创建单例成功 ...\n");                instance = new ThreadPool();            }        }        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 t(threadname, bind(&ThreadPool::ThreadRun, this, placeholders::_1), td);                        _threads.emplace_back(threadname, bind(&ThreadPool::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 _q;    vector> _threads;    int _thread_num;    pthread_mutex_t _mutex;    pthread_cond_t _cond;// 单例模式    static ThreadPool *instance;    static pthread_mutex_t sig_lock;};// 单例模式templateThreadPool *ThreadPool::instance = nullptr;templatepthread_mutex_t ThreadPool::sig_lock = PTHREAD_MUTEX_INITIALIZER;

实现功能:

代码语言:javascript代码运行次数:0运行复制
#include "LockGuard.hpp"#include "Log.hpp"#include "Thread.hpp"#include "Task.hpp"#include "ThreadPool.hpp"#include using namespace std;int main(){    sleep(10);    ThreadPool::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::GetInstance()->Push(t);        sleep(1);    }    ThreadPool::GetInstance()->Wait();    return 0;}
在这里插入图片描述

模拟实现线程池代码


?3. 其他常见的各种锁乐观锁:认为冲突的概率很低,先修改完共享资源再验证是否发生冲突。如果发生冲突,则放弃本次操作并重试。乐观锁全程没有加锁操作,因此也称为无锁编程悲观锁:认为多线程同时修改共享资源的概率较高,容易出现冲突。因此,在访问共享资源前会先上锁。前面提到的互斥锁、自旋锁、读写锁都属于悲观锁自旋锁(Spinlock): 用途:保护共享资源,确保在任何时刻只有一个线程或进程可以访问 特点:当线程试图获取锁时,如果锁已被占用,则线程会一直尝试获取锁,处于忙等待状态,直到成功为止。自旋锁适用于临界区较小且共享资源独占时间较短的场景,可以避免上下文切换的开销。但自旋锁不能用于需要睡眠的代码临界区,因为会占用CPU资源读写锁(Rwlock): 用途:解决读操作较多的场景,减少锁等待,提高并发性 特点:分为读锁和写锁。读锁允许多个线程同时读取共享资源,而写锁则确保只有一个线程可以写入资源。写锁会阻塞其他读写锁。读写锁适用于读操作远多于写操作的场景CAS操作:内存地址、期望值和新值。它将内存地址中的值与期望值进行比较,如果一致,则将内存中的值更新为新值,并返回成功标识;如果不一致,则不更新内存中的值,并返回失败标识。这个操作是原子的,意味着它不会被其他线程打断,从而保证了数据的一致性

?4. 读者写者问题

读写锁:

在这里插入图片描述

注意:写独占,读共享,读锁优先级高


核心规则:

写-写互斥:不能有两个写者同时进行写操作读-写互斥:不能同时有一个线程在读,而另一个线程在写读-读允许:可以有一个或多个读者在读

读者写者问题和生产者消费者模型大差不差,同样满足我们之前说的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); // 解锁
?5. 总结

随着本文的逐渐收尾,我们对Linux线程池的学习之旅也即将告一段落。但请记住,这仅仅是一个开始,而非终点。线程池作为并发编程中的核心组件,其深度和广度远超我们在这篇简短文章中所能涵盖的范围

愿你在未来的学习和工作中,能够充分利用线程池技术,打造出更加高效、稳定的并发应用!

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行! 谢谢大家支持本篇到这里就结束了,祝大家天天开心!


# linux  # 操作系统  # ai  # switch  # 无锁  # JavaScript  # 接口  # 线程  # 多线程  # 并发  # 在这里  # 加锁  # 插入图片  # 多个  # 适用于  # 只有一个  # 互斥  # 较短  # 发生冲突 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  香港服务器如何优化才能显著提升网站加载速度?  Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)  晋江文学城电脑版官网 晋江文学城网页版直接进入  Swift中switch语句区间和元组模式匹配  什么是javascript作用域_全局和局部作用域有什么区别?  做企业网站制作流程,企业网站制作基本流程有哪些?  高端建站如何打造兼具美学与转化的品牌官网?  Laravel如何使用Livewire构建动态组件?(入门代码)  微信公众帐号开发教程之图文消息全攻略  Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】  iOS验证手机号的正则表达式  Laravel队列任务超时怎么办_Laravel Queue Timeout设置详解  Laravel集合Collection怎么用_Laravel集合常用函数详解  Laravel如何处理CORS跨域请求?(配置示例)  魔毅自助建站系统:模板定制与SEO优化一键生成指南  Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言  如何用VPS主机快速搭建个人网站?  青岛网站建设如何选择本地服务器?  深圳网站制作平台,深圳市做网站好的公司有哪些?  Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程  如何利用DOS批处理实现定时关机操作详解  Laravel如何发送邮件_Laravel Mailables构建与发送邮件的简明教程  WordPress 子目录安装中正确处理脚本路径的完整指南  Laravel用户密码怎么加密_Laravel Hash门面使用教程  猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】  如何在万网自助建站平台快速创建网站?  Laravel怎么使用Intervention Image库处理图片上传和缩放  免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?  Laravel如何记录自定义日志?(Log频道配置)  C++用Dijkstra(迪杰斯特拉)算法求最短路径  mc皮肤壁纸制作器,苹果平板怎么设置自己想要的壁纸我的世界?  大同网页,大同瑞慈医院官网?  Laravel如何生成和使用数据填充?(Seeder和Factory示例)  iOS正则表达式验证手机号、邮箱、身份证号等  Android实现代码画虚线边框背景效果  韩国服务器如何优化跨境访问实现高效连接?  详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南  百度输入法ai组件怎么删除 百度输入法ai组件移除工具  Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】  如何用y主机助手快速搭建网站?  如何在Windows环境下新建FTP站点并设置权限?  历史网站制作软件,华为如何找回被删除的网站?  宙斯浏览器视频悬浮窗怎么开启 边看视频边操作其他应用教程  Laravel软删除怎么实现_Laravel Eloquent SoftDeletes功能使用教程  Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧  电商网站制作价格怎么算,网上拍卖流程以及规则?  阿里云网站搭建费用解析:服务器价格与建站成本优化指南  制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?  Laravel如何与Inertia.js和Vue/React构建现代单页应用