文章目录
- 进程与线程的通信方式全面总结
- [第一部分:线程通信(Thread Communication)](#第一部分:线程通信(Thread Communication))
-
- [1. 全局变量共享(最简单方式)](#1. 全局变量共享(最简单方式))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [2. 互斥锁(Mutex):解决互斥问题](#2. 互斥锁(Mutex):解决互斥问题)
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [3. 信号量(Semaphore):互斥 + 同步二合一](#3. 信号量(Semaphore):互斥 + 同步二合一)
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [4. 条件变量(Condition Variable)](#4. 条件变量(Condition Variable))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [5. 读写锁(RWLock)](#5. 读写锁(RWLock))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [6. 屏障(Barrier)](#6. 屏障(Barrier))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- 第二部分:进程通信(IPC)
-
- [1. 匿名管道(pipe)](#1. 匿名管道(pipe))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [2. 命名管道(FIFO)](#2. 命名管道(FIFO))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [3. 消息队列(Message Queue)](#3. 消息队列(Message Queue))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [4. 共享内存(Shared Memory):最快 IPC](#4. 共享内存(Shared Memory):最快 IPC)
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [✔ 使用方法](#✔ 使用方法)
- [5. 信号量(semget / semop)](#5. 信号量(semget / semop))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [6. 信号(signal)](#6. 信号(signal))
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [7. 套接字(socket)](#7. 套接字(socket))
-
- [① UNIX 域套接字 ------ 本地进程通信最快方式](#① UNIX 域套接字 —— 本地进程通信最快方式)
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 使用方法](#✔ 使用方法)
- [② TCP/IP 套接字 ------ 支持跨机器通信](#② TCP/IP 套接字 —— 支持跨机器通信)
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- [8. mmap 文件映射](#8. mmap 文件映射)
-
- [✔ 使用场景](#✔ 使用场景)
- [✔ 优点](#✔ 优点)
- [✔ 缺点](#✔ 缺点)
- 第三部分:两者对比总结(非常重要)
- 最终总结(选择建议)
-
- [✔ 如果是线程之间通信:](#✔ 如果是线程之间通信:)
- [✔ 如果是进程之间通信:](#✔ 如果是进程之间通信:)
进程与线程的通信方式全面总结
(使用场景 + 优缺点 + 使用方式)
在 Linux 系统中,程序的并发执行通常依赖 进程(Process) 或 线程(Thread) 。
它们在运行时需要共享数据、协作执行,这就产生了:
- 进程间通信(IPC)
- 线程间通信(同步与共享数据)
本篇文章通过系统化的方式,为你总结所有通信方式、各自适用的场景、优缺点,并给出示例代码,帮助你快速掌握 Linux 下的并发编程。
第一部分:线程通信(Thread Communication)
线程共享同一进程空间,它们本身没有"通信",而是共享变量 + 使用同步原语控制访问顺序。
线程通信的核心是:
✔ 共享内存
✔ 同步机制保证线程安全
常用方式如下:
1. 全局变量共享(最简单方式)
✔ 使用场景
- 多个线程共享某个变量(计数器、状态标志等)
✔ 优点
- 简单、自然、不需要系统调用
- 速度最快(线程共享内存)
✔ 缺点
- 必须使用锁保护
- 容易产生数据竞争(race condition)
✔ 使用方法
c
int shared_value = 0;
void* worker(void* arg) {
shared_value++;
return NULL;
}
2. 互斥锁(Mutex):解决互斥问题
✔ 使用场景
- 多线程访问共享资源时要保护临界区
- 解决:同时只有一个线程能执行关键代码
✔ 优点
- 机制简单
- 用途广泛、稳定可靠
✔ 缺点
- 使用不当容易造成死锁
- 不能解决同步(等待条件)问题
✔ 使用方法
c
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);
3. 信号量(Semaphore):互斥 + 同步二合一
✔ 使用场景
- 线程同步(例如生产者-消费者)
- 控制可同时访问资源的线程数量(计数信号量)
✔ 优点
- 能实现互斥,也能实现同步
- 支持计数,比 mutex 更灵活
✔ 缺点
- 比 mutex 更难理解
- 使用不当可能导致死锁
✔ 使用方法
c
sem_t sem;
sem_init(&sem, 0, 1);
sem_wait(&sem); // P 操作
// 临界区
sem_post(&sem); // V 操作
4. 条件变量(Condition Variable)
✔ 使用场景
- 一个线程等待某个条件成立(如 shared_data > 0)
- 广泛用于生产者-消费者模型
✔ 优点
- 比忙等(while循环)高效
- 能使线程睡眠直到被唤醒
✔ 缺点
- 必须配合互斥锁使用
- 逻辑比 mutex 略复杂
✔ 使用方法
c
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
while (shared_data == 0)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
5. 读写锁(RWLock)
✔ 使用场景
- 多个线程需要同时读取共享数据(读多写少)
✔ 优点
- 允许多个线程读,提高并发度
- 写操作仍然保证互斥
✔ 缺点
- 写锁容易饥饿
- 使用复杂度比 mutex 高
✔ 使用方法
c
pthread_rwlock_t rwlock;
pthread_rwlock_rdlock(&rwlock);
// 读操作
pthread_rwlock_unlock(&rwlock);
6. 屏障(Barrier)
✔ 使用场景
- 一组线程需要在某个同步点汇合,然后一起继续执行
✔ 优点
- 解决"一次性全体同步"问题
- 在多阶段算法中常用(并行计算)
✔ 缺点
- 不常用于业务逻辑
- 使用起来稍复杂
✔ 使用方法
c
pthread_barrier_t barrier;
pthread_barrier_init(&barrier, NULL, thread_num);
pthread_barrier_wait(&barrier);
第二部分:进程通信(IPC)
进程完全隔离,不共享内存,因此需要借助内核的 IPC 机制交换数据。
按难度从简单到复杂排列:
1. 匿名管道(pipe)
✔ 使用场景
- 父子进程通信
- shell 的
ls | grep就是管道
✔ 优点
- 简单易用
- 单向数据流很适合流水线处理
✔ 缺点
- 只能用于父子进程
- 单向流动、不能同时双向发
✔ 使用方法
c
int fd[2];
pipe(fd);
write(fd[1], "hello", 5);
read(fd[0], buf, 5);
2. 命名管道(FIFO)
✔ 使用场景
- 任意两个进程通信
- 简单客户端-服务端模型
✔ 优点
- 支持无关进程通信
- API 简单
✔ 缺点
- 同样是单向
- 不适合彩信大量数据
✔ 使用方法
c
mkfifo("myfifo", 0666);
int fd = open("myfifo", O_WRONLY);
write(fd, "hi", 2);
3. 消息队列(Message Queue)
✔ 使用场景
- 多个进程之间发送消息
- 适合小型命令通信(非大量数据)
✔ 优点
- 有消息边界,不会出现黏包问题
- 内核管理消息队列,稳定可靠
✔ 缺点
- 队列大小有限制
- 不适合大量数据传输
4. 共享内存(Shared Memory):最快 IPC
✔ 使用场景
- 大量数据交换(视频、音频、图片等)
- 性能要求极高的应用
✔ 优点
- 最快的 IPC(几乎零开销)
- 数据直接在同一块内存中共享
✔ 缺点
- 必须使用同步机制(如信号量)
- 编程复杂度略高
✔ 使用方法
c
int shmid = shmget(1234, 1024, 0666|IPC_CREAT);
char* mem = shmat(shmid, NULL, 0);
// 读写 mem 即可
5. 信号量(semget / semop)
✔ 使用场景
- 进程间同步(配合共享内存)
- 避免多个进程同时访问共享资源
✔ 优点
- 操作非常高效
- 跨进程同步能力强
✔ 缺点
- 只能做同步,不传数据
- 使用略复杂
6. 信号(signal)
✔ 使用场景
- 进程间发送异步事件(比如 kill 通知)
✔ 优点
- 实现简单
- 适合发送控制指令(终止进程)
✔ 缺点
- 只能传递简单事件
- 不适合传输数据
7. 套接字(socket)
分两种:
① UNIX 域套接字 ------ 本地进程通信最快方式
✔ 使用场景
- 本地客户端-服务端
- Nginx、Docker、MySQL 常用
✔ 优点
- 和 TCP 几乎一样,但更快
- 使用文件路径作为地址
✔ 使用方法
c
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
bind(sock, ... "/tmp/test.sock");
② TCP/IP 套接字 ------ 支持跨机器通信
✔ 使用场景
- 网络程序必须用它(Web 服务器、聊天程序)
✔ 优点
- 可跨主机通信
- API 通用、功能强大
✔ 缺点
- 效率比 UDS 和共享内存低
- 涉及网络协议栈处理
8. mmap 文件映射
✔ 使用场景
- 文件共享数据
- 多进程共享内存但使用文件作为底层媒介
✔ 优点
- 实现共享内存的另一种方式
- 适合大量数据
✔ 缺点
- 需要文件系统支持
- 不适合频繁的同步操作
第三部分:两者对比总结(非常重要)
| 通信类型 | 适用对象 | 速度 | 是否共享内存 | 工程常用性 |
|---|---|---|---|---|
| 线程通信 | 同一进程内线程 | 🚀 非常快 | ✔ 是 | 极常用 |
| 进程通信 | 完全独立的进程 | ⚡ 中等 | ❌ 否 | 常用 |
最终总结(选择建议)
✔ 如果是线程之间通信:
优先选择:
共享变量 → mutex → cond → sem → rwlock
✔ 如果是进程之间通信:
顺序建议如下:
大量数据 → 共享内存(最快)
本地通信 → UNIX 域套接字
跨主机 → TCP/IP socket
简单消息 → 队列
通知事件 → signal
父子进程 → pipe