并发编程中的竞争条件

下面关于竞争条件的描述,来自《现代操作系统》

在一些操作系统中,协作的进程可能共享一些彼此都能读写的公用存储区。这个公用存储区可能在内存中(可能是在内核数据结构中),也可能是一个共享文件。这里共享存储区的位置并不影响通信的本质及其带来的问题。为了理解实际中进程间通信如何工作,我们考虑一个简单但很普遍的例子:一个假脱机打印程序。当一个进程需要打印一个文件时,它将文件名放在一个特殊的假脱机目录 (spooler directory)下。另一个进程(打印机守护进程)则周期性地检查是否有文件需要打印,若有就打印并将该文件名从目录下删掉。

设想假脱机目录中有许多槽位,编号依次为0,1,2,…,每个槽位存放一个文件名。同时假设有两个共享变量:out,指向下一个要打印的文件;in,指向目录中下一个空闲槽位。可以把这两个变量保存在一个所有进程都能访问的文件中,该文件的长度为两个字。在某一时刻,0号至3号槽位空(其中的文件已经打印完毕),4号至6号槽位被占用(其中存有排好队列的要打印的文件名)。几乎在同一时刻,进程A和进程B都决定将一个文件排队打印,这种情况如图2-21所示。

 

在Murphy法则(任何可能出错的地方终将出错)生效时,可能发生以下的情况。进程A读到in的值为7,将7存在一个局部变量next_free_slot中。此时发生一次时钟中断,CPU认为进程A已运行了足够长的时间,决定切换到进程B。进程B也读取in,同样得到值为7,于是将7存在B的局部变量next_free_slot中。在这一时刻两个进程都认为下一个可用槽位是7。

进程B现在继续运行,它将其文件名存在槽位7中并将in的值更新为8。然后它离开,继续执行其他操作。

最后进程A接着从上次中断的地方再次运行。它检查变量next_free_slot,发现其值为7,于是将打印文件名存入7号槽位,这样就把进程B存在那里的文件名覆盖掉。然后它将next_free_slot加1,得到值为8,就将8存到in中。此时,假脱机目录内部是一致的,所以打印机守护进程发现不了任何错误,但进程B却永远得不到任何打印输出。类似这样的情况,即两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序,称为竞争条件(race condition)。调试包含有竞争条件的程序是一件很头痛的事。大多数的测试运行结果都很好,但在极少数情况下会发生一些无法解释的奇怪现象。

时间: 2024-08-02 06:12:55

并发编程中的竞争条件的相关文章

并发编程中的阻塞队列概述

1.简介 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列.这两个附加的操作支持阻塞的插入和移除方法. 1)支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满. 2)支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空. 阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程.阻塞队列就是生产者用来存放元素.消费者用来获取元素的容器. 在阻塞队列不可用时,插入.移除这两个附加操作提供4种处理

理解并发编程中的重要概念:指令重排序和指令乱序执行

看过了很多介绍指令重排序的文章,可惜由于自己硬件和计算机理论知识缺乏,很难理解深层次的奥秘和实现原理.不过也有很多帖子,讲的浅显易懂,使用的例子很形象.大牛就是能用简单的解释和通俗的比喻,给我们讲明白很高深的东西.这里做个摘抄和总结,和大家分享下,希望大家能够对指令重排序有个形象的认识,不至于在并发编程中犯一些简单的错误.如果理解有错误,希望看到的大神指正. 从源码变成可以被机器(或虚拟机)识别的程序,至少要经过编译期和运行期.重排序分为两类:编译期重排序和运行期重排序(乱序执行),分别对应编译

如何减少并发编程中的上下文切换

一.首先,看看有什么工具可以度量上下文切换带来的消耗. 使用Lmbench3(一个性能分析工具)可以测量上下文切换时长 使用vmstat可以测量上下文切换的次数. 例如:在lunix命令界面:vmstat  2 1 CS(Content Switch)表示上下文切换的次数,从上面的测试结果中我们可以看到,上下文每一秒切换307次. 二.如何减少上下文切换 减少上下文切换的方法有无锁并发编程.CAS算法.使用最少线程和使用协程. 1. 无锁并发并发编程.多线程竞争锁时,会引起上下文切换,所以多线程

【Java并发编程】6、volatile关键字解析&内存模型&并发编程中三概念

转自:http://www.cnblogs.com/dolphin0520/p/3920373.html volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来

161212、并发编程中的关于队列

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据.同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者.为了解决这个问题于是引入了生产者和消费者模式. 什么是生产者消费者模式 生产者

并发编程中.net与java的一些对比

Java在并发编程中进行使用java.util.concurrent.atomic来处理一些轻量级变量 如AtomicInteger AtomicBoolean等 .Net中则使用Interlocked来实现类似功能 Java中使用object的wait和notify方法来实现线程间的写作 .Net中可以使用Semaphore(信号量).mutex.和EventWaitHandle来实现 但是Semaphore类的构造函数需要指定初始入口数和最大入口数

java并发编程中CountDownLatch和CyclicBarrier的使用

转自:http://blog.csdn.net/hbzyaxiu520/article/details/6183714 在多线程程序设计中,经常会遇到一个线程等待一个或多个线程的场景,遇到这样的场景应该如何解决? 如果是一个线程等待一个线程,则可以通过await()和notify()来实现: 如果是一个线程等待多个线程,则就可以使用CountDownLatch和CyclicBarrier来实现比较好的控制. 下面来详细描述下CountDownLatch的应用场景: 例如:百米赛跑:8名运动员同时

java并发编程中常用的工具类 Executor

/***************************************************  * TODO: description .  * @author: gao_chun  * @since:  2015-4-17  * @version: 1.0.0  * @remark: 转载请注明出处  **************************************************/ java.util.concurrent.Executor 使用 Execut

并发编程中,如何减少上下文切换

减少上下文切换的方法有无锁并发编程.CAS算法.使用最少线程和使用协程.1. 无锁并发编程.多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据.2. CAS算法.Java的Atomic包使用CAS算法来更新数据,而不需要加锁.3. 使用最少线程.避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态.4. 协程:在单线程里实现多任务的调度,并在单线程里维持多