← LINUX 分类
LINUX

不只互斥锁:七种进程同步思路

梳理信号量、管程、消息传递、无锁结构与 RCU 等并发同步方法的适用场景。


本文目录 · 5 节

同步解决什么问题

只要多个执行单元会同时访问同一份状态,就需要回答两个问题:谁能进入临界区,以及条件不满足时应该等待还是继续工作。互斥锁只是最常见的答案,并不是唯一答案。

同步方案之间的主要差异,在于是否共享内存、等待时是否占用 CPU、读写比例,以及程序愿意承担多少实现复杂度。

经典同步工具

纯软件互斥

Peterson 算法用两个意愿标志和一个轮次变量协调两个执行单元;Lamport 面包店算法则把取号排队推广到多个参与者。它们很适合理解互斥的必要条件,但现代 CPU 的乱序执行和内存模型意味着实际使用时仍需要内存屏障等支持。

信号量

信号量通过原子的 wait 与 signal 操作表达资源数量。计数为零时,线程可以阻塞而不是不断轮询,因此既能实现互斥,也能表达“生产完成后才能消费”这类先后关系。

管程与条件变量

管程把共享状态和允许的操作封装到一起,并保证同一时刻只有一个执行单元进入。条件变量负责“条件尚未满足时睡眠”和“状态改变后唤醒”。Java 的 synchronized、C# 的 lock 都体现了类似思路。

避免共享状态

消息传递不是保护共享内存,而是尽量不共享。CSP 模型通过通道交换数据,Actor 模型让每个 Actor 独立维护状态并接收异步消息。

这种设计把并发控制转化为通信协议,通常更容易隔离故障,也适合分布式系统。不过,消息顺序、超时、重试和背压仍然需要明确设计。

高性能方案

方法 核心思路 适合场景
事务内存 冲突时回滚整段操作 复杂的组合状态更新
无锁结构 使用 CAS 等原子操作重试 高并发队列、栈和计数器
RCU 读者直接读取,写者复制后替换指针 读多写少的数据结构

无锁不等于没有同步成本。ABA 问题、内存回收和活锁都可能让实现远比普通锁复杂。RCU 也要求写者等待旧读者离开后再回收旧版本。

怎样选择

  1. 普通应用优先使用语言或标准库提供的锁、条件变量和线程安全容器。
  2. 如果问题天然是事件流或独立服务之间协作,优先考虑消息传递。
  3. 只有性能测量确认锁竞争是瓶颈后,再考虑无锁结构或 RCU。
  4. 把正确性放在吞吐量之前:先写清共享状态、不变量、等待条件和退出条件。

真正重要的不是“有没有锁”,而是并发访问的规则是否清楚、可验证,并且在故障时仍能恢复。