信号量与管程

一、概述

 信号量是操作系统提供的一种协调共享资源访问的方法。和用软件实现的同步比较,软件同步是平等线程间的的一种同步协商机制,不能保证原子性。而信号量则由操作系统进行管理,地位高于进程,操作系统保证信号量的原子性。

   信号量是跟锁机制在同一个层次上的编程方法。

   管程是为了解决信号量在临界区的PV操作上的配对的麻烦,把配对的PV操作集中在一起,生成的一种并发编程方法。其中使用了条件变量这种同步机制。

    

二、信号量

  1)概述

  信号中包括一个整形变量,和两个原子操作P和V,其原子性由操作系统保证,这个整形变量只能通过P操作和V操作改变。

    P意味着信号量值减1,减完之后如果信号量值小于0,则说明资源不够用的,把进程加入等待队列。

  V意味着信号量值加1,加完之后如果信号量值小于等于0,则说明等待队列里有进程,那么唤醒一个等待进程。

    

    

  信号量的等待进程被放在等待队列中,按先进先出的次序执行。

    自旋锁不能保证进程按先进先出的次序执行,因为自旋锁的所有等待进程都在循环中忙等待,在临界区释放的那一刻,先检查到临界区为空的进程先进入临界区,没有先来后到之分。

  2)具体实现:

   

  3)信号量分类:

    i)二进制信号量:资源数目为0或1

    ii)资源信号量:资源数目为任何非负值

    两者其实是等价的,基于一个可以实现另一个。

  4) 信号量作用:

   i)实现临界区的互斥访问

     进入临界区之前使用P操作,如果信号量为1,则进入,且信号量设置为0。如果信号量为0,则进入等待队列。

     退出临界区之后使用V操作,信号量值加1,如果信号量还小于等于0,则唤醒等待队列中的一个进程。

     

  ii)实现条件同步

    一个线程A使用P操作,一个线程B使用V操作,初始的信号量设置为0。则为了满足某个条件,必须在线程B执行之后,才可以执行线程A。用信号量可以轻松实现这一点。

 

 5)用信号量实现生产者-消费者问题

  

     

    

 6)使用信号量的缺陷

    读/开发代码比较困难,而且PV在不同的线程里配对,容易写错。而且必须先检查资源信号量的值,再进入临界区(即先写emptyBuffers->P(),再写mutex->P()),否则所有线程都不能进入临界区。

三、管程

 1)概述

   管程是为了解决信号量在临界区的PV操作上的配对的麻烦,把配对的PV操作集中在一起,生成的一种并发编程方法。其中使用了条件变量这种同步机制。

   管程与临界区不同的是,在管程中的线程可以临时放弃管程的互斥访问,让其他线程进入到管程中来。而临界区中的线程只能在线程退出临界区时,才可以放弃对临界区的访问。

    

  2)管程的组成

    当条件变量的数目为0时,管程和临界区相同。

    

  3)条件变量

  一个条件变量就对应于一个等待队列,每个条件变量有一个Wait()操作和Signal()操作。

    

   

 4)用管程实现生产者-消费者问题

   

 先进入管程,再进行判断。就是因为管程中的线程可以放弃对管程的互斥访问,交由其他线程访问管程。

   (视频里说 管程可以把PV操作都集中到一个模块里,我对此有疑问。。在我看来,不就是把两个信号量表示成两个条件变量了嘛。。PV还是分散的啊。求指教)

  5)管程条件变量的释放处理方式

    进程A执行,被阻塞,进入等待队列,切换到进程B,当进程B执行signal操作使进程A可以执行时,有两种方式:一种是等进程B执行完再执行进程A,另一种是立即切换到进程A,等执行完进程A再切换到进程B,执行进程B。

    第二种比第一种多了一次切换,所以第一种比较高效,但第二种容易证明其正确性。第一种主要用于真实OS和Java中,第二种主要见于教材中。

     

四、哲学家就餐问题

  

   方案1:都先拿左边的叉子,再拿右边的叉子,拿不到就等着。当5个哲学家同时拿起左边的叉子时,就会引起死锁。

     

    方案2:某一时间内,只能有一个哲学家在进餐,其他的人都不许拿叉子。结果正确,但是效率低。

     

    方案3:按某一规律(下图是按奇偶数分类)让不同的哲学家先拿不同方向的叉子,即不是都先拿左手的叉子或右手的叉子,而是有的先拿左手的叉子,有的先拿右手的叉子。从而避免方案1中死锁的发生。

     

五、读者写者问题

   

   

   

 

     

    

    

    

    

posted @ 2016-05-30 20:21  kk_kk  阅读(7451)  评论(1编辑  收藏