线程间通信
c++ 线程间通信方式可分为两大类:
- 两个进程中的两个线程通信方式:共享内存、管道(无名/有名)、消息队列、信号量、Socket、信号(Signal)、共享文件(存储映射区)
- 一个进程中的两个线程间通信方式:互斥锁、条件变量、读写锁、信号量(线程级)、自旋锁、屏障、Future/Promise
💡关键区别:
- 跨进程通信需通过操作系统内核介入(如管道、Socket),数据需跨地址空间传递。
- 进程内通信直接通过共享内存地址访问,依赖同步机制避免竞争。
跨进程通信方式详解
以下表格总结了主要跨进程通信方式的特点和适用场景:
通信方式 | 特点 | 适用场景 | 同步需求 |
---|---|---|---|
共享内存 | 直接映射相同物理内存,速度最快;需手动同步(如信号量) | 高频数据交换(如实时视频处理) | 高(需额外同步机制) |
管道 | 半双工,分无名管道(亲缘进程)和有名管道(任意进程);数据流式传输 | 父子进程间简单数据传递(如Shell命令流水线) | 中(内核缓冲区同步) |
消息队列 | 异步通信,消息带格式和优先级;内核持久化存储 | 解耦生产/消费者(如日志系统) | 低(队列自带同步) |
信号量 | 计数器,控制多进程对共享资源的访问 | 资源池管理(如数据库连接池) | 高(需配合锁) |
Socket | 支持网络/本地通信(UNIX域套接字),全双工 | 分布式系统或本地进程间结构化通信 | 中(协议层同步) |
信号(Signal) | 内核向进程发送事件通知(如SIGKILL ) | 异常处理或进程控制 | 无(异步事件) |
共享文件 | 通过文件系统读写(如内存映射文件) | 大数据持久化共享(需处理读写冲突) | 高(需文件锁) |
进程内线程通信方式详解
以下表格总结了主要进程内线程通信方式的特点和适用场景:
通信方式 | 特点 | 适用场景 | 性能影响 |
---|---|---|---|
互斥锁(Mutex) | 独占访问共享资源,阻塞等待 | 临界区保护(如全局计数器) | 中(线程切换开销) |
条件变量(Condition Variable) | 需搭配互斥锁,通过wait() /notify() 同步状态 | 生产者-消费者模型 | 低(避免忙等待) |
读写锁 | 允许多读/单写,读优先或写优先 | 配置信息读取(90%读+10%写) | 低(读并发优化) |
信号量(线程级) | 控制并发线程数(如线程池) | 资源限流(如数据库连接限制) | 中 |
自旋锁(Spin Lock) | 忙等待而非阻塞,避免上下文切换 | 极短临界区(如原子操作) | 高(CPU占用) |
屏障(Barrier) | 多线程同步至同一阶段后再继续 | 并行计算分阶段(如MapReduce) | 中 |
Future/Promise | 异步操作结果传递(C++11标准) | 异步任务链(如网络请求+数据处理) | 低 |
关键总结
- 分类核心原则:
- 跨进程通信 依赖操作系统机制(如管道、Socket)。
- 进程内通信 依赖线程同步原语(如锁、条件变量)。
- 选型建议:
- 追求性能 → 优先考虑共享内存(跨进程)或无锁结构(进程内)。
- 需要解耦 → 使用消息队列(跨进程)或Future/Promise(进程内)。
- 简单场景 → 互斥锁+条件变量 组合可覆盖多数需求。
- 常见误区:
- 信号量既可用于进程间也可用于线程间,但实现不同(进程级信号量需内核支持)。
- 管道在跨进程通信中效率较低,高频场景应首选共享内存+信号量。