竞争条件与互斥

本文详细探讨了多线程编程中如何处理竞争条件,介绍了互斥的概念,包括忙等待的互斥(如关闭中断、锁变量、Peterson解法、TSL上锁)和睡眠等待(如信号量、互斥锁),并以生产者消费者问题为例,阐述了信号量在解决同步问题中的应用。同时,文章通过银行汇款的例子展示了如何使用互斥锁避免竞争条件,确保多线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要讲解多线程(进程)编程时出现竞争条件的解决方案及代码实现。

竞争条件

协同进程可能共享一些彼此都能够读写的公用存储区(例如打印机脱机系统),也可能是一些共享文件,当两个或多个进程读写某些共享数据,而最后的结果却绝育进程运行的精确时序,就称为竞争条件(race condition)

如果程序之间有竞争条件,也许大部分的运行结果都很好,但在极少数情况下会发生一些难以解释的事情。

互斥(mutual exclusion)

要避免这种错误,关键是要找出某种途径防止多个进程在使用一个共享变量或文件时,其他进程不能做同样的事。

互斥的实现有很多种,在UNIX编程中,总体来说有两种大的方案:

  1. 忙等待形式的互斥
    • 优势在于被等待的进程(线程)不需要context switch(no extra overhead),提高了效率
    • 但若等待时间较长,会浪费CPU资源。
    • 会造成优先级反转问题(priority inversion problem)
  2. 睡眠等待
    • CPU利用率较高,但会造成context switch的overhead。

临界区

把对共享内存进行访问的程序片段称为临界区或者临界段(critical region)。

如果能够进行适当安排,使得两个进程不可能同时处于临界区,则能够避免竞争条件。

我们认为一个好的方案应该能解决竞争条件的同时,依然高效地进行操作,满足以下四个条件:

  1. 任何两个进程不能同时处于临界区。
  2. 不应该对CPU的速度和树木做任何假设。
  3. 临界区外的进程不得阻塞其他进程。
  4. 不能使进程在临界区外无休止的等待。

忙等待的互斥

关闭中断

这是最简单也最直接的方案,使得每个进程在进入临界区后先关闭中断,在离开之前再打开中断。

中断被关闭后,时钟中断也会关闭。因此CPU在做完临界区之前都不会发生进程切换。

缺点

  1. 把关闭中断的权利交给用户进程是不明智的。可能会造成系统终止。
  2. 不适用于多CPU情形。
  3. 在实际中很少采用。

关闭中断对于操作系统是一项很有用的技术,但对于用户进程不是一种合适的通用互斥机制。

锁变量

设想有一个共享锁变量,在进程想要进入临界区时,先测试这把锁。

但可以想象,如果锁变量依然是普通类型(不是原子类型),则依然会发生竞争条件。

严格交替法

首先看示意代码:

/// process 0 ////
while(true){  
    while(turn!=0);  
    critical_region();  
    turn=1;  
    noncritical_region();  
}  
/// process 1 ////
while(true){  
    while(turn!=1);  
    critical_region();  
    turn=0;  
    noncritical_region();  
}  

process 0 必须在 turn 变量等于0时才会进入临界区,process 1 必须在 turn 变量等于1时才能进入临界区。

假设turn变量初始化为0,则process 1会一直持续地检测一个变量,直到为1才执行下面的代码。这种等待为忙等待。一个适用于忙等待的锁称为自旋锁(spin lock)

仔细观察以上代码,两个进程互相依靠对方提供的turn变量才能继续下去。若一个进程的noncrit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值