ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux 之 【多线程】(线程池、单例模式)

Linux 之 【多线程】(线程池、单例模式) 目录1.线程池概念优点工作流程代码实现总结2.单例模式概念特点两种实现方式饿汉模式懒汉方式线程安全的懒汉方式双重判空C11后的最简实现局部静态成员要点总结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::queuestd::shared_ptrT tasks_; auto task_ptr std::make_sharedT(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保证异常时资源不泄露 * * 线程安全是多生产者/多消费者 */ templateclass 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_sharedT(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_sharedT(std::move(task)); LockGuard guard(mutex_); tasks_.push(std::move(task_ptr)); pthread_cond_signal(cond_); } /** * brief 获取当前任务队列大小不精确仅供参考 */ size_t size() const { LockGuard guard(const_castpthread_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_castThreadPool*(arg); while (!pool-shutdown_.load()) { std::shared_ptrT 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::vectorpthread_t threads_; // 线程ID列表 int thread_count_; // 线程数量 // 任务管理 std::queuestd::shared_ptrT tasks_; // 任务队列智能指针自动管理生命周期 std::atomicbool shutdown_; // 优雅关机标志原子操作内存可见性 };总结线程池 生产者消费者模型 池化技术生产者提交任务消费者执行任务队列缓冲任务池化复用线程——以空间换时间以控制换稳定线程池的作用与实现原理的简述线程池的作用是复用线程以降低开销、控制并发度。线程池通过复用预先创建的线程避免了频繁创建销毁的开销从而提升系统性能同时通过限制最大线程数防止资源耗尽在吞吐量和稳定性之间取得平衡实现上它包含一个预先创建的线程组和一个任务队列。利用‘互斥锁’保护队列的临界区利用‘条件变量’实现线程的等待与唤醒当队列空时线程休眠有任务时唤醒线程执行从而实现高效的生产者-消费者模型。2.单例模式概念单例模式是一种创建型设计模式确保一个类只有一个实例并提供一个全局访问点特点唯一性某个类只应该有一个对象实例全局访问提供统一的全局访问接口典型应用场景服务器加载大量数据上百G到内存时用单例类管理这些数据两种实现方式饿汉模式class Singleton { public: static Singleton getInstance() { return instance; } private: Singleton() default; Singleton(const Singleton) delete; Singleton operator(const Singleton) delete; static Singleton instance; }; Singleton Singleton::instance;特点程序加载时就创建实例类比吃完饭立刻洗碗下一顿直接可用优点实现简单线程安全缺点无论是否使用都会创建可能造成资源浪费懒汉方式class Singleton { public: // 线程不安全 static Singleton* getInstance() { if (instance nullptr) { instance new Singleton(); } return instance; } private: Singleton() default; Singleton(const Singleton) delete; Singleton operator(const Singleton) delete; static Singleton* instance; }; Singleton* Singleton::instance nullptr;特点首次使用时才创建实例类比吃完饭先放着下顿用时再洗核心思想延迟加载优化启动速度C11后的最简实现局部静态成员class Singleton { public: static Singleton getInstance() { static Singleton instance; return instance; } private: Singleton() default; Singleton(const Singleton) delete; Singleton operator(const Singleton) delete; };C11保证局部静态对象初始化的线程安全性代码简洁只需初始化一次不需要显式加锁要点总结构造函数私有化防止外部创建对象拷贝构造和赋值私有防止拷贝产生新对象静态成员/静态方法提供全局访问点延迟加载懒汉模式的核心优势优化启动速度线程安全多线程环境下保证单一实例双重判空提高性能的关键技术volatile防止指令重排序和优化问题
返回列表