目录
1.线程池
概念
线程池 是一种池化技术 ,通过预先创建一组线程,重复利用线程执行多个任务,避免频繁创建/销毁线程的开销
本质 :以空间换时间------用内存预分配换取CPU调度的效率
优点
| 优势 | 原理 | 收益 |
|---|---|---|
| 降低开销 | 复用线程,避免重复创建/销毁 | 短任务场景性能提升10~100倍 |
| 提高响应速度 | 线程已就绪,任务到达即执行 | 无需等待线程创建,延迟降低 |
| 控制并发度 | 线程数量上限固定 | 防止资源耗尽,系统更稳定 |
| 任务管理 | 任务队列缓冲 | 平滑突发流量,削峰填谷 |
| 统一管理 | 监控、调优、扩展集中化 | 运维友好,可观测性强 |
工作流程
1. 初始化阶段
└── 预创建 corePoolSize 个工作线程,全部阻塞在任务队列上
2. 任务提交阶段
└── 提交者(生产者)将任务封装成 Runnable/Task
└── 任务队列.push(task)
3. 任务执行阶段
└── 工作线程(消费者):
├── while(1) {
├── task = 任务队列.pop() // 队列空则阻塞
├── task.run()
└── }
4. 扩容阶段(动态线程池)
└── 队列满 && 线程数 < maxPoolSize → 创建新线程
5. 缩容阶段
└── 线程空闲超过 keepAliveTime → 销毁线程
6. 销毁阶段
└── shutdown():不再接受新任务,执行完已有任务后退出
└── shutdownNow():立即停止所有任务,返回未执行任务列表
代码实现
- 线程入口函数
在类内定义线程入口函数时需要将其定义为静态成员函数,不然就会有隐藏的this指针导致参数不匹配
在start函数中传递 this 指针 作为入口函数的参数,这样线程入口函数就可以调用类内成员了
-
RAII锁
class LockGuard {
explicit LockGuard(pthread_mutex_t& mutex) {
pthread_mutex_lock(&mutex);
}
~LockGuard() {
pthread_mutex_unlock(&mutex);
}
};
即使在锁内return或抛异常,也会自动解锁,杜绝了"忘记解锁"导致的死锁,代码简洁,不需要手动写unlock
-
锁外执行任务
{
LockGuard guard(pool->mutex_);
task_ptr = std::move(pool->tasks_.front());
pool->tasks_.pop();
} // ← 锁在这里释放!// 锁外执行耗时任务
if (task_ptr) {
(*task_ptr)();
}
锁持有时间从"任务执行时间"降到"队列操作时间",支持长任务不阻塞其他线程取任务。任务抛异常不会影响线程池状态
-
智能指针管理任务生命周期
std::queue<std::shared_ptr<T>> tasks_;
auto task_ptr = std::make_shared<T>(std::move(task));
自动释放内存,无需手动delete,线程异常退出时,未执行的任务自动释放,还可以存储派生类任务
此外,双关机既可以保证保证数据一致性,又可以用于紧急停止场景移动语义优化大对象拷贝的性能
try、catch 保证异常安全的任务执行
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <memory>
#include <atomic>
#include <functional>
#include <pthread.h>
#include <unistd.h>
/**
* @brief 线程池 - 工业级实现
*
* 核心特性:
* - 优雅关机:原子标志 + 条件变量广播唤醒
* - RAII资源管理:LockGuard自动加解锁
* - 移动语义:任务支持右值传递,零拷贝优化
* - 任务生命周期:智能指针管理,自动释放
* - 异常安全:RAII保证异常时资源不泄露
*
* 线程安全:是(多生产者/多消费者)
*/
template<class T>
class ThreadPool {
public:
/**
* @brief 构造函数
* @param num 线程数量,默认5
*/
explicit ThreadPool(int num = 5)
: shutdown_(false)
, thread_count_(num) {
if (num <= 0) {
throw std::invalid_argument("Thread count must be positive");
}
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
/**
* @brief 禁止拷贝/移动 - 包含pthread句柄
*/
ThreadPool(const ThreadPool&) = delete;
ThreadPool& operator=(const ThreadPool&) = delete;
ThreadPool(ThreadPool&&) = delete;
ThreadPool& operator=(ThreadPool&&) = delete;
/**
* @brief 析构函数 - 自动优雅关机
*/
~ThreadPool() {
shutdown(); // 1. 唤醒所有线程并等待退出
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
/**
* @brief 启动线程池
*/
void start() {
LockGuard guard(mutex_);
if (!threads_.empty()) return; // 防止重复启动
for (int i = 0; i < thread_count_; ++i) {
pthread_t tid;
pthread_create(&tid, nullptr, worker, this);
threads_.emplace_back(tid);
}
}
/**
* @brief 优雅关闭线程池
*
* 1. 设置关机标志(原子操作,保证内存可见性)
* 2. 广播唤醒所有等待线程
* 3. 等待所有线程执行完当前任务后退出
* 4. 清空任务队列(由任务智能指针自动释放)
*/
void shutdown() {
shutdown_.store(true);
{
LockGuard guard(mutex_);
pthread_cond_broadcast(&cond_); // 唤醒所有等待线程
}
// 等待所有线程退出
for (pthread_t tid : threads_) {
pthread_join(tid, nullptr);
}
threads_.clear();
}
/**
* @brief 立即关闭线程池
*
* 1. 设置关机标志
* 2. 广播唤醒所有线程
* 3. 清空任务队列(未执行的任务将被销毁)
*/
void shutdownNow() {
shutdown_.store(true);
{
LockGuard guard(mutex_);
// 清空任务队列,智能指针自动释放任务资源
while (!tasks_.empty()) {
tasks_.pop();
}
pthread_cond_broadcast(&cond_);
}
for (pthread_t tid : threads_) {
pthread_join(tid, nullptr);
}
threads_.clear();
}
/**
* @brief 提交任务(左值版本)
* @param task 任务对象
*/
void push(const T& task) {
if (shutdown_.load()) return;
// 使用智能指针管理任务生命周期
auto task_ptr = std::make_shared<T>(task);
LockGuard guard(mutex_);
tasks_.push(std::move(task_ptr));
pthread_cond_signal(&cond_); // 唤醒一个工作线程
}
/**
* @brief 提交任务(右值版本,移动语义优化)
* @param task 任务对象
*/
void push(T&& task) {
if (shutdown_.load()) return;
// 移动构造任务,零拷贝优化
auto task_ptr = std::make_shared<T>(std::move(task));
LockGuard guard(mutex_);
tasks_.push(std::move(task_ptr));
pthread_cond_signal(&cond_);
}
/**
* @brief 获取当前任务队列大小(不精确,仅供参考)
*/
size_t size() const {
LockGuard guard(const_cast<pthread_mutex_t&>(mutex_));
return tasks_.size();
}
/**
* @brief 线程池是否已关闭
*/
bool isShutdown() const {
return shutdown_.load();
}
private:
/**
* @brief RAII锁守卫
*
* 优点:异常安全,自动解锁,防止遗漏
* 原理:构造加锁,析构解锁
*/
class LockGuard {
public:
explicit LockGuard(pthread_mutex_t& mutex) : mutex_(mutex) {
pthread_mutex_lock(&mutex_);
}
~LockGuard() {
pthread_mutex_unlock(&mutex_);
}
private:
pthread_mutex_t& mutex_;
};
/**
* @brief 工作线程入口函数
* @param arg 线程池对象指针
* @return nullptr
*
* 核心逻辑:
* 1. 检查关机标志,决定是否退出
* 2. 等待任务队列非空(条件变量)
* 3. 取出任务智能指针
* 4. 在锁外执行任务(减少锁持有时间)
*/
static void* worker(void* arg) {
ThreadPool* pool = static_cast<ThreadPool*>(arg);
while (!pool->shutdown_.load()) {
std::shared_ptr<T> task_ptr;
{
LockGuard guard(pool->mutex_);
// while循环防止伪唤醒
while (pool->tasks_.empty() && !pool->shutdown_.load()) {
pthread_cond_wait(&pool->cond_, &pool->mutex_);
}
// 关机检查:如果已关机且队列空,线程退出
if (pool->shutdown_.load() && pool->tasks_.empty()) {
break;
}
// 取出任务(移动语义,避免拷贝)
task_ptr = std::move(pool->tasks_.front());
pool->tasks_.pop();
} // 解锁,锁持有时间极短
//锁外执行任务!
// 1. 减少锁竞争
// 2. 支持长任务不阻塞其他线程
// 3. 异常安全(任务抛异常不影响线程池状态)
if (task_ptr) {
try {
(*task_ptr)(); // 执行任务
} catch (const std::exception& e) {
std::cerr << "Task execution failed: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Task execution failed with unknown error" << std::endl;
}
}
}
return nullptr;
}
private:
// 同步原语
pthread_mutex_t mutex_; // 互斥锁:保护任务队列
pthread_cond_t cond_; // 条件变量:任务队列非空等待
// 线程管理
std::vector<pthread_t> threads_; // 线程ID列表
int thread_count_; // 线程数量
// 任务管理
std::queue<std::shared_ptr<T>> tasks_; // 任务队列(智能指针自动管理生命周期)
std::atomic<bool> shutdown_; // 优雅关机标志(原子操作,内存可见性)
};
总结
线程池 = 生产者消费者模型 + 池化技术
生产者提交任务,消费者执行任务,队列缓冲任务,池化复用线程------以空间换时间,以控制换稳定
2.单例模式
概念
单例模式 是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点
特点
唯一性:某个类只应该有一个对象(实例)
全局访问:提供统一的全局访问接口
典型应用场景:服务器加载大量数据(上百G)到内存时,用单例类管理这些数据
两种实现方式
饿汉模式
template <typename T>
class Singleton {
static T data;
public:
static T* GetInstance() {
return &data;
}
};
特点:程序加载时就创建实例
类比:吃完饭立刻洗碗,下一顿直接可用
优点:实现简单,线程安全
缺点:无论是否使用都会创建,可能造成资源浪费
懒汉方式
// 基础版本(线程不安全)
template <typename T>
class Singleton {
static T* inst;
public:
static T* GetInstance() {
if (inst == NULL) {
inst = new T(); // 线程不安全点
}
return inst;
}
};
特点:首次使用时才创建实例
类比:吃完饭先放着,下顿用时再洗
核心思想:延迟加载,优化启动速度
线程安全的懒汉方式(双重判空)
template <typename T>
class Singleton {
volatile static T* inst; // volatile防止编译器优化
static std::mutex lock;
public:
static T* GetInstance() {
if (inst == NULL) { // 第一次判空:避免不必要的锁竞争
lock.lock(); // 加锁保证线程安全
if (inst == NULL) { // 第二次判空:防止多次创建
inst = new T();
}
lock.unlock();
}
return inst;
}
};
双重判空:外层判空减少锁竞争,内层判空保证唯一创建
互斥锁:保证多线程环境下只调用一次new
volatile关键字:防止编译器过度优化
| 优化类型 | 问题描述 | volatile的作用 |
|---|---|---|
| 寄存器缓存 | 变量值缓存在寄存器,忽略内存修改 | 强制每次从内存读取 |
| 指令重排 | 对象构造和赋值重排导致未完全构造的对象被使用 | 禁止相关指令重排 |
| 删除冗余读 | 优化掉必要的判空操作 | 保持所有读写操作 |
| 常量折叠 | 把多次读取优化为常量 | 保证每次读取真实值 |
C++11后的最简实现(局部静态成员)
class Singleton {
private:
// 构造函数私有
Singleton() {}
// 拷贝构造和赋值私有
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& GetInstance() {
static Singleton instance; // 局部静态对象
return instance;
}
};
C++11保证局部静态对象初始化的线程安全性
代码简洁,只需初始化一次
不需要显式加锁
要点总结
构造函数私有化 :防止外部创建对象
拷贝构造和赋值私有 :防止拷贝产生新对象
静态成员/静态方法 :提供全局访问点
延迟加载 :懒汉模式的核心优势,优化启动速度
线程安全 :多线程环境下保证单一实例
双重判空 :提高性能的关键技术
volatile:防止指令重排序和优化问题