Linux 并发编程:从线程池到单例模式的深度实践

在这里插入图片描述

文章目录

    • 一、普通线程池:高效线程管理的核心方案
      • 1. 线程池概念:为什么需要 "线程工厂"?
      • 2. 线程池的实现:从 0 到 1 构建基础框架
    • 二、模式封装:跨语言线程库实现
      • 1. C++ 模板化实现:类型安全的泛型设计
      • 2. Python 线程池:利用标准库快速实现
      • 3. C 语言原生实现:POSIX 线程深度控制
    • 三、线程安全的单例模式:全局资源的唯一守护
      • 1. 单例模式与设计模式:为什么需要 "全局唯一"?
      • 2. 饿汉模式 VS 懒汉模式:初始化时机的博弈
        • 饿汉模式(Eager Initialization)
        • 懒汉模式(Lazy Initialization)
      • 3. 懒汉模式的线程安全改造:双重检查锁定
    • 四、STL、智能指针与线程安全:现代 C++ 的最佳实践
      • 1. STL 容器的线程安全边界
      • 2. 智能指针的线程安全特性
    • 五、锁机制大全:选择最合适的并发控制
    • 六、读者写者问题:读写锁的实战应用
      • 1. 问题引入:如何优化读多写少场景?
      • 2. 读写锁接口解析(POSIX 标准)
      • 3. 样例代码:实现读者优先策略
    • 总结:构建健壮并发系统的核心法则

在云计算、微服务架构盛行的今天,高并发编程已成为后端开发的核心战场。如何高效管理线程资源?怎样确保全局资源的唯一访问?本文将通过「线程池」与「单例模式」两大核心技术,带您揭开 Linux 并发编程的神秘面纱,附完整代码解析与最佳实践。

一、普通线程池:高效线程管理的核心方案

1. 线程池概念:为什么需要 “线程工厂”?

传统线程模型中,每次任务都伴随pthread_create/pthread_join的开销。对于高频短任务场景(如 Web 服务器请求处理),频繁的线程创建销毁会导致惊人的性能损耗。线程池通过「预先创建 - 重复利用 - 动态管理」的机制,将线程生命周期与任务解耦,实现:

  • 降低资源开销:避免线程创建销毁的内核态上下文切换

  • 控制并发数量:防止因线程过多导致的系统资源耗尽

  • 任务队列缓冲:平滑处理突发流量高峰

2. 线程池的实现:从 0 到 1 构建基础框架

// 线程池结构体定义
struct ThreadPool {int thread_count;       // 工作线程数量pthread_t* threads;     // 线程句柄数组pthread_mutex_t lock;   // 任务队列互斥锁pthread_cond_t cond;    // 任务通知条件变量bool shutdown;          // 关闭标志Queue* task_queue;      // 任务队列(自定义环形队列或链表)
};// 任务函数原型
typedef void* (*TaskFunc)(void* arg);// 任务结构体
typedef struct {TaskFunc func;          // 具体任务函数void* arg;              // 函数参数
} Task;// 工作线程主函数
void* worker_thread(void* arg) {ThreadPool* pool = (ThreadPool*)arg;while (1) {pthread_mutex_lock(&pool->lock);// 无任务且未关闭时等待while (is_queue_empty(pool->task_queue) && !pool->shutdown) {pthread_cond_wait(&pool->cond, &pool->lock);}// 处理关闭信号if (pool->shutdown && is_queue_empty(pool->task_queue)) {pthread_mutex_unlock(&pool->lock);pthread_exit(NULL);}// 取出任务Task* task = dequeue(pool->task_queue);pthread_mutex_unlock(&pool->lock);// 执行任务task->func(task->arg);free(task);}return NULL;
}// 提交任务接口
bool add_task(ThreadPool* pool, TaskFunc func, void* arg) {Task* task = (Task*)malloc(sizeof(Task));task->func = func;task->arg = arg;pthread_mutex_lock(&pool->lock);enqueue(pool->task_queue, task);pthread_cond_signal(&pool->cond);  // 唤醒等待线程pthread_mutex_unlock(&pool->lock);return true;
}

核心机制解析

  1. 任务队列:使用互斥锁保证线程安全,条件变量实现无任务时的阻塞等待

  2. 线程管理:通过shutdown标志实现优雅关闭,避免线程僵死

  3. 资源回收:任务执行完毕后释放内存,防止内存泄漏

二、模式封装:跨语言线程库实现

1. C++ 模板化实现:类型安全的泛型设计

template <typename T>
class ThreadPool {
private:int thread_count;std::vector<std::thread> threads;std::queue<T> task_queue;std::mutex mtx;std::condition_variable cv;bool stop = false;public:explicit ThreadPool(int num = std::thread::hardware_concurrency()): thread_count(num) {for (int i = 0; i < thread_count; ++i) {threads.emplace_back([this]() {  // lambda表达式作为线程函数while (true) {std::unique_lock<std::mutex> lock(this->mtx);this->cv.wait(lock, [this]() { return this->stop || !this->task_queue.empty(); });if (this->stop && this->task_queue.empty()) return;T task = std::move(this->task_queue.front());this->task_queue.pop();lock.unlock();task();  // 执行具体任务(可调用仿函数或lambda)}});}}~ThreadPool() {{std::lock_guard<std::mutex> lock(mtx);stop = true;}cv.notify_all();  // 唤醒所有线程for (auto& thread : threads) thread.join();  // 等待所有线程结束}template <typename F>void enqueue(F&& f) {{std::lock_guard<std::mutex> lock(mtx);task_queue.emplace(std::forward<F>(f));}cv.notify_one();  // 唤醒一个工作线程}
};

C++ 特性应用

  • std::thread封装 POSIX 线程,简化线程管理

  • std::condition_variable实现更简洁的等待通知机制

  • 移动语义std::move优化任务传递效率

2. Python 线程池:利用标准库快速实现

from concurrent.futures import ThreadPoolExecutor
import time# 定义任务函数
def task(n):time.sleep(1)return n * n# 创建线程池(最大工作线程数5)
with ThreadPoolExecutor(max_workers=5) as executor:# 提交单个任务future = executor.submit(task, 5)print(future.result())  # 输出25# 批量提交任务results = [executor.submit(task, i) for i in range(10)]for res in results:print(res.result())# 底层实现关键点:
# 1. _WorkItem队列用于任务缓存
# 2. ThreadPoolExecutor管理工作线程生命周期
# 3. Future对象封装异步结果获取

3. C 语言原生实现:POSIX 线程深度控制

// 基于POSIX线程的极简实现(省略任务队列具体实现)
void* worker(void* arg) {thread_pool_t* pool = (thread_pool_t*)arg;while (1) {// 加锁获取任务pthread_mutex_lock(&pool->lock);// 无任务时等待while (pool->queue_size == 0 && !pool->shutdown) {pthread_cond_wait(&pool->cond, &pool->lock);}// 处理关闭逻辑if (pool->shutdown && pool->queue_size == 0) {pool->active_threads--;pthread_mutex_unlock(&pool->lock);pthread_exit(NULL);}// 取出任务task_t* t = dequeue(pool);pthread_mutex_unlock(&pool->lock);// 执行任务t->func(t->arg);free(t->arg);free(t);}return NULL;
}

三、线程安全的单例模式:全局资源的唯一守护

1. 单例模式与设计模式:为什么需要 “全局唯一”?

单例模式(Singleton Pattern)确保一个类仅有一个实例,并提供全局访问点。典型应用场景:

  • 日志管理器:全局唯一的日志输出实例

  • 配置读取器:避免重复加载配置文件

  • 线程池实例:全局共享的线程资源池

2. 饿汉模式 VS 懒汉模式:初始化时机的博弈

饿汉模式(Eager Initialization)
// 编译期初始化,线程安全(C++11之后)
class Singleton {
private:Singleton() = default;~Singleton() = default;Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton instance;  // 静态成员变量public:static Singleton& get_instance() {return instance;}
};
Singleton Singleton::instance;  // 全局初始化

优点:简单直接,无需加锁

缺点:不管是否使用都会提前创建

懒汉模式(Lazy Initialization)
// 非线程安全版本(危险!)
class Singleton {
private:static Singleton* instance;Singleton() = default;
public:static Singleton* get_instance() {if (instance == nullptr) {  // 第一次检查instance = new Singleton();}return instance;}
};
Singleton* Singleton::instance = nullptr;

线程安全问题:多个线程同时通过第一次检查时,会创建多个实例

3. 懒汉模式的线程安全改造:双重检查锁定

#include <mutex>class Singleton {
private:static Singleton* instance;static std::mutex mtx;Singleton() = default;~Singleton() { delete instance; }public:static Singleton* get_instance() {if (instance == nullptr) {  // 第一次检查(无锁快速路径)std::lock_guard<std::mutex> lock(mtx);  // 加锁if (instance == nullptr) {  // 第二次检查(防止重复创建)instance = new Singleton();// C++11之后需要防止指令重排,需添加std::atomic或__volatile__}}return instance;}
};
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

关键优化

  1. 双重检查:减少锁竞争,仅在第一次创建时加锁

  2. C++11 保证:静态局部变量初始化的线程安全性

// C++11推荐写法(更简洁的线程安全实现)
class Singleton {
public:static Singleton& get_instance() {static Singleton instance;  // 局部静态变量,线程安全初始化return instance;}
private:Singleton() = default;~Singleton() = default;Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};

四、STL、智能指针与线程安全:现代 C++ 的最佳实践

1. STL 容器的线程安全边界

  • 无锁访问:单个线程读 / 写容器是安全的

  • 跨线程操作:多个线程同时访问需加锁保护

std::vector<int> data;
std::mutex data_mutex;// 线程A
{std::lock_guard<std::mutex> lock(data_mutex);data.push_back(10);
}// 线程B
{std::lock_guard<std::mutex> lock(data_mutex);int value = data.back();
}

2. 智能指针的线程安全特性

  • std::unique_ptr:独占所有权,跨线程移动时需加锁

  • std::shared_ptr:引用计数原子操作保证线程安全

std::shared_ptr<ThreadPool> pool;
std::mutex pool_mutex;// 安全获取单例化线程池
std::shared_ptr<ThreadPool> get_pool() {std::lock_guard<std::mutex> lock(pool_mutex);if (!pool) {pool = std::make_shared<ThreadPool>(10);}return pool;
}

五、锁机制大全:选择最合适的并发控制

锁类型适用场景优势劣势
互斥锁 (pthread_mutex)通用场景实现简单可能导致线程上下文切换
自旋锁 (pthread_spinlock)锁持有时间极短无上下文切换忙等待消耗 CPU
读写锁 (pthread_rwlock)读多写少场景允许多个读锁并发写操作饥饿问题
递归锁 (pthread_mutex_recursive)同一线程多次加锁防止死锁性能略低于普通互斥锁
// 自旋锁典型应用(内核态常用)
pthread_spinlock_t spinlock;
pthread_spinlock_init(&spinlock, PTHREAD_PROCESS_SHARED);// 线程A尝试加锁
while (pthread_spin_trylock(&spinlock) != 0) {// 忙等待直到获取锁
}
// 临界区操作
pthread_spin_unlock(&spinlock);

六、读者写者问题:读写锁的实战应用

1. 问题引入:如何优化读多写少场景?

当共享资源被频繁读取(如配置文件、字典数据),传统互斥锁会成为性能瓶颈。读者写者问题的解决方案需满足:

  • 允许多个读者同时访问

  • 写者具有互斥访问权

  • 可选策略:读者优先(可能导致写者饥饿)或写者优先

2. 读写锁接口解析(POSIX 标准)

// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);// 读锁获取(共享锁)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);// 写锁获取(独占锁)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);// 解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

3. 样例代码:实现读者优先策略

pthread_rwlock_t rwlock;
int shared_data = 0;// 读者线程函数
void* reader(void* arg) {int id = *(int*)arg;for (int i = 0; i < 5; ++i) {pthread_rwlock_rdlock(&rwlock);  // 获取读锁printf("Reader %d: Data = %d\n", id, shared_data);pthread_rwlock_unlock(&rwlock);  // 释放读锁usleep(100000);  // 模拟读操作耗时}return NULL;
}// 写者线程函数
void* writer(void* arg) {int id = *(int*)arg;for (int i = 0; i < 3; ++i) {pthread_rwlock_wrlock(&rwlock);  // 获取写锁shared_data++;printf("Writer %d: Updated Data = %d\n", id, shared_data);pthread_rwlock_unlock(&rwlock);  // 释放写锁usleep(200000);  // 模拟写操作耗时}return NULL;
}int main() {pthread_rwlock_init(&rwlock, NULL);// 创建3个读者线程和2个写者线程pthread_t readers[3], writers[2];int ids[5] = {1, 2, 3, 4, 5};for (int i = 0; i < 3; ++i) {pthread_create(&readers[i], NULL, reader, &ids[i]);}for (int i = 0; i < 2; ++i) {pthread_create(&writers[i], NULL, writer, &ids[i+3]);}for (int i = 0; i < 3; ++i) {pthread_join(readers[i], NULL);}for (int i = 0; i < 2; ++i) {pthread_join(writers[i], NULL);}pthread_rwlock_destroy(&rwlock);return 0;
}

执行效果

  • 多个读者可同时持有读锁,提升并发读性能

  • 写操作时独占锁,确保数据一致性

  • 通过pthread_rwlock_tryrdlock可实现非阻塞读锁获取

总结:构建健壮并发系统的核心法则

  1. 线程池优先:避免重复造轮子,善用成熟框架(如 C++ 的std::async、Python 的concurrent.futures)

  2. 单例模式慎用:仅在真正需要全局唯一实例时使用,优先考虑依赖注入等更灵活模式

  3. 锁策略优化:根据场景选择锁类型,尽量缩小临界区范围

  4. 现代工具链:充分利用 C++11 的原子操作、智能指针,Python 的 GIL 规避技巧

通过合理组合线程池与单例模式,结合精准的锁控制,我们能在高并发场景下实现资源的高效利用与数据的安全访问。记住:并发编程的本质是「控制复杂度」,而非单纯追求性能 —— 清晰的架构设计永远比复杂的锁逻辑更重要。

现在,尝试将本文的线程池与单例模式结合,设计一个全局唯一的异步任务调度中心吧!遇到具体实现问题时,欢迎在评论区留言讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/84183.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/84183.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2013年SEVC SCI2区,自适应变领域搜索算法Adaptive VNS+多目标设施布局,深度解析+性能实测

目录 1.摘要2.自适应局部搜索原理3.自适应变领域搜索算法Adaptive VNS4.结果展示5.参考文献6.代码获取7.算法辅导应用定制读者交流 1.摘要 VNS是一种探索性的局部搜索方法&#xff0c;其基本思想是在局部搜索过程中系统性地更换邻域。传统局部搜索应用于进化算法每一代的解上&…

详细介绍医学影像显示中窗位和窗宽

在医学影像&#xff08;如DICOM格式的CT图像&#xff09;中&#xff0c;**窗宽&#xff08;Window Width, WW&#xff09;和窗位&#xff08;Window Level, WL&#xff09;**是两个核心参数&#xff0c;用于调整图像的显示对比度和亮度&#xff0c;从而优化不同组织的可视化效果…

Unity_VR_如何用键鼠模拟VR输入

文章目录 [TOC] 一、创建项目1.直接创建VR核心模板&#xff08;简单&#xff09;2.创建3D核心模板导入XR包 二、添加XR设备模拟器1.打开包管理器2.添加XR设备模拟器3.将XR设备模拟器拖到场景中4.运行即可用键盘模拟VR输入 一、创建项目 1.直接创建VR核心模板&#xff08;简单&…

SpringBoot定时监控数据库状态

1.application.properties配置文件 # config for mysql spring.datasource.url jdbc\:mysql\://127.0.0.1\:3306/数据库名?characterEncoding\utf8&useSSL\false spring.datasource.username 账号 spring.datasource.password 密码 spring.datasource.validation-quer…

Qt联合Halcon开发一:Qt配置Halcon环境【详细图解流程】

在Qt中使用Halcon库进行图像处理开发&#xff0c;可以有效地结合Qt的图形界面和Halcon强大的计算机视觉功能。下面是详细的配置过程&#xff0c;帮助你在Qt项目中成功集成Halcon库。 步骤 1: 安装Halcon软件并授权 首先&#xff0c;确保你已经在电脑上安装了Halcon软件&#x…

一体化(HIS系统)医院信息系统,让医疗数据互联互通

在医疗信息化浪潮下&#xff0c;HIS系统、LIS系统、PACS系统、电子病历系统等信息系统成为医疗机构必不可少的一部分&#xff0c;从患者挂号到看诊&#xff0c;从各种检查到用药&#xff0c;从院内治疗到院外管理……医疗机构不同部门、不同科室的各类医疗、管理业务几乎都初步…

Spring Boot 的 3 种二级缓存落地方式

在高并发系统设计中&#xff0c;缓存是提升性能的关键策略之一。随着业务的发展&#xff0c;单一的缓存方案往往无法同时兼顾性能、可靠性和一致性等多方面需求。 此时&#xff0c;二级缓存架构应运而生&#xff0c;本文将介绍在Spring Boot中实现二级缓存的三种方案。 一、二…

Android Studio Profiler使用

一:memory 参考文献: AndroidStudio之内层泄漏工具Profiler使用指南_android studio profiler-CSDN博客

Zephyr boot

<!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Zephyr设备初始化机制交互式解析…

腾讯地图Web版解决热力图被轮廓覆盖的问题

前言 你好&#xff0c;我是喵喵侠。 还记得那天傍晚&#xff0c;我正对着电脑调试一个腾讯地图的热力图页面。项目是一个区域人流密度可视化模块&#xff0c;我加了一个淡蓝色的轮廓图层用于表示区域范围&#xff0c;热力图放在下面用于展示人流热度。效果一预览&#xff0c;…

【JVMGC垃圾回收场景总结】

文章目录 CMS在并发标记阶段&#xff0c;已经被标记的对象&#xff0c;又被新生代跨带引用&#xff0c;这时JVM会怎么处理?为什么 Minor GC 会发生 STW&#xff1f;有哪些对象是在栈上分配的&#xff1f;对象在 JVM 中的内存结构为什么需要对齐填充&#xff1f;JVM 对象分配空…

3_STM32开发板使用(STM32F103ZET6)

STM32开发板使用(STM32F103ZET6) 一、概述 当前所用开发板为正点原子精英板,MCU: STM32F103ZET6。一般而言,拿到板子之后先要对板子有基础的认识,包括对开发板上电开机、固件下载、调试方法这三个部分有基本的掌握。 二、系统开机 2.1 硬件连接 直接接电源线或Type-c线…

crackme012

crackme012 名称值软件名称attackiko.exe加壳方式无保护方式serial编译语言Delphi v1.0调试环境win10 64位使用工具x32dbg,PEid破解日期2025-06-18 -发现是 16位windows 程序环境还没搭好先留坑

CppCon 2016 学习:I Just Wanted a Random Integer

你想要一个随机整数&#xff0c;用于模拟随机大小的DNA读取片段&#xff08;reads&#xff09;&#xff0c;希望覆盖不同长度范围&#xff0c;也能测试边界情况。 代码部分是&#xff1a; #include <cstdlib> auto r std::rand() % 100;它生成一个0到99之间的随机整数&…

MySQL层级查询实战:无函数实现部门父路径

本次需要击毙的MySQL函数 函数主要用于获取部门的完整层级路径&#xff0c;方便在应用程序或SQL查询中直接调用&#xff0c;快速获得部门的上下级关系信息。执行该函数之后简单使用SQL可以实现数据库中部门名称查询。例如下面sql select name,GetDepartmentParentNames(du.de…

Python初学者教程:如何从文本中提取IP地址

Python初学者教程:如何从文本中提取IP地址 在网络安全和数据分析领域,经常需要从文本文件中提取IP地址。本文将引导您使用Python创建一个简单但实用的工具,用于从文本文件提取所有IP地址并将其保存到新文件中。即使您是编程新手,也可以跟随本教程学习Python的基础知识! …

【Redis】Redis核心探秘:数据类型的编码实现与高速访问之道

&#x1f4da;️前言 &#x1f31f;&#x1f31f;&#x1f31f;精彩导读 本次我们将全面剖析Redis的核心技术要点&#xff0c;包括其丰富的数据类型体系、高效的编码方式以及秒级响应的性能奥秘。对于渴望深入理解Redis底层机制的技术爱好者&#xff0c;这是一次难得的学习机会…

Halcon —— 多种二维码检测

工业视觉实战&#xff1a;Halcon多类型二维码识别技术详解 在工业自动化场景中&#xff0c;兼容多种二维码类型是提高生产线灵活性的关键。本文将深入解析Halcon实现Data Matrix、QR Code和PDF417三种主流二维码的兼容识别方案&#xff0c;并重点解释核心算子参数。 一、多类型…

安卓vscodeAI开发实例

前言 前些天发现了一个巨牛的人工智能免费学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 目录 一、安卓开发基础与工具链革新 1.1 Android Studio的局限性分析 1.2 VSCode在移动开发中的崛起 1.3 跨平台开发工具链对比…

③通用搜索---解析FastAdmin中的表格列表的功能

深度解析FastAdmin中的表格列表的功能-CSDN博客文章浏览阅读25次。本文将FastAdmin框架的CRUD功能配置要点进行了系统梳理。官方文档与开发经验相结合&#xff0c;详细介绍了菜单显示、TAB过滤、通用搜索、工具栏按钮、动态统计、快速搜索等17项功能的配置方法。包括字段渲染&a…