Linux:线程池 - 指南

1.1日志与策略模式

策略模式

策略模式是一种 ​​行为型设计模式​​,它定义了一系列算法(策略),并将每个算法封装成独立的类,使它们可以互相替换。策略模式让算法的变化独立于使用它的客户端。

日志认识
计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件,主要作⽤是监控运⾏状态、记录异常信
息,帮助快速定位问题并⽀持程序员进⾏问题修复。它是系统维护、故障排查和安全管理的重要⼯
具。
⽇志格式以下⼏个指标是必须得有的
  • 时间戳
  • ⽇志等级
  • ⽇志内容
以下⼏个指标是可选的
  • ⽂件名⾏号
  • 进程,线程相关id信息等
这⾥我们采⽤设计模式-策略模式来进⾏⽇志的设计
我们想要的⽇志格式如下:
[可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] - 消息内容,⽀持
可变参数
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [17] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [18] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [20] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [21] - hello world
[2024-08-04 12:27:03] [WARNING] [202938] [main.cc] [23] - hello world
Log.hpp
#ifndef __LOG_HPP__
#define __LOG_HPP__
#include
#include
#include
#include  //C++17
#include
#include
#include
#include
#include
#include "Mutex.hpp"
namespace LogModule
{
using namespace MutexModule;
const std::string gsep = "\r\n";
// 策略模式,C++多态特性
// 2. 刷新策略 a: 显示器打印 b:向指定的文件写入
//  刷新策略基类
class LogStrategy
{
public:
virtual void SyncLog(const std::string& message) = 0;
~LogStrategy() = default;
};
// 显示器打印日志的策略 : 子类
class ConsoleLogStrategy : public LogStrategy
{
public:
ConsoleLogStrategy() {}
void SyncLog(const std::string& message) override
{
LockGuard lockguard(_mutex);
std::cout ();
}
void EnableConsoleLogStrategy()
{
_fflush_strategy = std::make_unique();
}
// 表示的是未来的一条日志
class LogMessage
{
public:
LogMessage(LogLevel& level, std::string& src_name, int line_number, Logger& logger)
: _curr_time(GetTimeStamp()),
_level(level),
_pid(getpid()),
_src_name(src_name),
_line_number(line_number),
_logger(logger)
{
std::stringstream ss;
ss
// LogMessage() SyncLog(_loginfo);
}
}
private:
std::string _curr_time;
LogLevel _level;
pid_t _pid;
std::string _src_name;
int _line_number;
std::string _loginfo; // 合并之后,一条完整的信息
Logger& _logger;
};
// 这里故意写成返回临时对象
LogMessage operator()(LogLevel level, std::string name, int line)
{
return LogMessage(level, name, line, *this);
}
~Logger()
{
}
private:
std::unique_ptr _fflush_strategy;
};
// 全局日志对象
Logger logger;
// 使用宏,简化用户操作,获取文件名和行号
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}
#endif
样例
LOG(LogLevel::DEBUG) << "hello world";
这⾥我们直接⽤我们⾃⼰封装的锁

2.线程池设计

线程池:
⼀种线程使⽤模式。线程过多会带来调度开销,进⽽影响缓存局部性和整体性能。⽽线程池维护着多个线程,等待着监督管理者分配可并发执⾏的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利⽤,还能防⽌过分调度。可⽤线程数量应该取决于可⽤的并发处理器、处理器内核、内存、⽹络sockets等的数量。
线程池的应用场景:
  • 需要⼤量的线程来完成任务,且完成任务的时间⽐较短。 ⽐如WEB服务器完成⽹⻚请求这样的任务,使⽤线程池技术是⾮常合适的。因为单个任务⼩,⽽任务数量巨⼤,你可以想象⼀个热⻔⽹站的点击次数。 但对于⻓时间的任务,⽐如⼀个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间⽐线程的创建时间⼤多了。
  • 对性能要求苛刻的应⽤,⽐如要求服务器迅速响应客⼾请求。
  • 接受突发性的⼤量请求,但不⾄于使服务器因此产⽣⼤量线程的应⽤。突发性⼤量客⼾请求,在没有线程池情况下,将产⽣⼤量线程,虽然理论上⼤部分操作系统线程数⽬最⼤值不是问题,短时间内产⽣⼤量线程可能使内存到达极限,出现错误。
线程池的种类:
  1. 创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执⾏任务对象中的任务接⼝
  2. 浮动线程池,其他同上
此处,我们选择固定线程个数的线程池。

ThreadPool.hpp
#pragma once
#include
#include
#include
#include
#include "Log.hpp"
#include "Thread.hpp"
#include "Cond.hpp"
#include "Mutex.hpp"
// 单例模式
namespace ThreadPoolModule
{
using namespace ThreadModlue;
using namespace LogModule;
using namespace CondModule;
using namespace MutexModule;
static const int gnum = 5;
template
class ThreadPool
{
private:
void WakeUpAllThread()
{
LockGuard lockguard(_mutex);
if (_sleeper)
_cond.Broadcast();
LOG(LogLevel::INFO)  &) = delete;
ThreadPool &operator=(const ThreadPool &) = delete;
public:
static ThreadPool *GetInstance()
{
if (inc == nullptr)
{
LockGuard lockguard(_lock);
LOG(LogLevel::DEBUG) ();
inc->Start();
}
}
return inc;
}
// 第一次判断单例是否存在,防止无意义竞争锁降低性能
void Stop()
{
if (!_isrunning)
return;
_isrunning = false;
// 唤醒所有的线程
WakeUpAllThread();
}
void Join()
{
for (auto &thread : _threads)
{
thread.Join();
}
}
void HandlerTask()
{
char name[128];
pthread_getname_np(pthread_self(), name, sizeof(name));
while (true)
{
T t;
{
LockGuard lockguard(_mutex);
// 1. a.队列为空 b. 线程池没有退出
while (_taskq.empty() && _isrunning)
{
_sleeper++;
_cond.Wait(_mutex);
_sleeper--;
}
// 2. 内部的线程被唤醒
if (_taskq.empty() && !_isrunning)
{
LOG(LogLevel::INFO)  _threads;
int _num; // 线程池中,线程的个数
std::queue _taskq;
Mutex _mutex;
Cond _cond;
bool _isrunning;
int _sleeper;
static ThreadPool *inc; // 单例指针
static Mutex _lock;
};
template
ThreadPool *ThreadPool::inc = nullptr;
template
Mutex ThreadPool::_lock;
}

posted on 2025-10-09 17:26  slgkaifa  阅读(12)  评论(0)    收藏  举报

导航