597 字
3 分钟
什么是竞态条件和临界区?如何保证互斥
当程序结果取决于多个执行流交错运行的时序时,可能出现竞态条件。共享变量的读改写常见地包含多条机器指令,例如 count++ 可拆成读、加一、写回;两个线程都读到 0 后各写入 1,最终值为 1 而非预期的 2。
这段可能读写共享状态的代码叫临界区。临界区保护通常希望满足:互斥(同一时刻至多一个执行流进入)、进展(无人占用时,选择下一个进入者不能无限拖延)、有限等待(请求者最终有机会进入)。简单地“加一把锁”能实现互斥,但仍要注意死锁、饥饿和锁粒度。
保护方法
最常见做法是用互斥锁保护共享状态,并确保所有读写该状态的路径都遵守同一锁协议。无锁方案可以用原子操作或不可变数据结构,但需要正确处理内存可见性、ABA 等问题。把共享数据复制到线程私有区域再合并,也能减少竞争,但须定义冲突解决规则。
临界区应尽量短;持锁时避免网络调用、磁盘 I/O、等待用户输入或调用未知回调。否则一个慢操作会让其他线程长时间排队。互斥与同步不同:互斥控制同一时刻能否进入;同步还要求执行顺序或等待某个条件,例如消费者等待队列非空。
常见追问
Q:volatile 能解决竞态吗?
A:通常不能。它一般不提供复合读改写的原子性,也不等同于互斥锁。具体语言内存模型各有规定,应使用该语言的原子类型或同步原语。
Q:给每个变量都加锁就安全吗?
A:不一定。多个变量之间的不变量可能需要同一把锁保护;不同锁的获取顺序还可能造成死锁。锁保护的范围应对应数据不变量,而非只对应单个字段。
Q:竞态一定会导致错误吗?
A:不一定每次都会暴露,但执行结果不可预测且难复现。若并发读写没有正确同步,即使测试暂时通过,也不能据此证明安全。
什么是竞态条件和临界区?如何保证互斥
https://blog.xqcherry.top/posts/interview/operating-system/race-condition-critical-section/ 目录