zoukankan      html  css  js  c++  java
  • 操作系统中的同步互斥机制总结

    1. 互斥与同步的概念
    互斥和同步是两个紧密相关而又容易混淆的概念。

    互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
    同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

    同步:任务间的直接制约关系,A要继续执行需要B完成某一个操作操作才能继续进行。
    互斥:任务间的间接制约关系,A访问了资源B就不能去访问,必须等A访问完了才行。

    2. 临界区

    在并发进程环境中,因为引入了中断,可能导致某些本该原子执行的指令序列被中断,而使得程序运行结果不确定,解决的方法就是保证一次只有一个进程执行这些指令(一次只允许一个进程进入临界区)。

    临界区:进程中访问临界资源的一段需要互斥执行的代码。对临界区的访问要确保如下规则:

    • 空闲则入:没有进程在临界区时,任何进程可进入
    • 忙则等待:有进程在临界区时,其他进程均不能进入临界区
    • 有限等待:等待进入临界区的进程不能无限期等待
    • 让权等待(可选):不能进入临界区的进程,应释放CPU(如转换到阻塞状态)

     临界区的实现方法
    (1)方法1:禁用硬件中断(仅限于单处理器)

    (2)方法2:基于软件的解决方法(实现复杂)

    • 比如:Peterson算法、Dekkers算法、Eisenberg和McGuire算法

    (3)方法3:更高级的抽象方法(单处理器或多处理器均可)

    硬件提供了一些原子操作指令,比如测试和置位(Test-and-Set )指令、交换指令(exchange),硬件的支持使得我们可以提供更高级的抽象解决方法。比如锁机制。

    3. 锁机制

    3.1 基本概念

    锁是一个更高等级的编程抽象。

    包含一个二进制变量(锁定/解锁),两个操作:

    Lock::Release()释放锁,唤醒任何等待的进程
    Lock::Acquire()锁被释放前一直等待,然后得到锁

    3.2 锁的实现

    具体实现锁的时候可以实现为两种方式:

    (1)有忙等待:在获取锁的时候,如果锁已经被获取了,则进程一直进行空循环,适合临界区执行时间短的情况。

    (2)无忙等待。获取锁的时候,如果锁已经被获取了,则进程放到等待队列,CPU进行上下文切换,执行其他进程。适合临界区执行费时的情况。

     使用Test-and-Set指令实现Acquire和Release操作:

     使用Exchange指令实现进入临界区和退出临界区操作:

    3.3 锁的应用

    锁机制可以用于互斥问题。

    4. 信号量

    4.1 信号量的概念

    信号量最早提出的时候就是为了解决操作系统中的同步互斥问题。

    4.2 信号量的实现

    实现类似于锁机制,但有点不同于锁机制,信号量的实现一般是用等待队列。

    4.3 信号量的双用途

    • 互斥问题
    • 同步问题

    5. 条件变量

    5.1 条件变量的概念

    条件变量是管程内的等待机制
    进入管程的线程因资源被占用而进入等待状态
    每个条件变量表示一种等待原因,对应一个等待队列

    Wait()操作
    释放锁,让其他线程有机会执行,自己睡眠。

    Signal()操作
    唤醒等待者。

    5.2 条件变量的实现

     

    6.管程

    6.1 管程的概念

    管程最早提出的时候是针对编程语言层面,不是操作系统层面。

    管程的组成:

    一个锁:控制管程代码的互斥访问。
    0或者多个条件变量:等待/通知信号量用于管理共享数据的并发访问。

    可参考维基百科

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

    Hansen管程当前的优先级更高T2,主要用于真实OS,Java;

    Horare管程等待条件变量的优先级更高T1,用于教科书中。

    6.3 Java对管程的支持

    Object类的如下方法:
    void notify() :唤醒在此对象监视器上等待的单个线程。
    void notifyAll() :唤醒在此对象监视器上等待的所有线程。
    void wait(): 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

    7. 生产者消费者问题
    (1)用信号量解决
    先找出有哪些约束:
    互斥约束:一次只能有一个进程操作缓冲区
    同步约束:1)缓冲区为空时,消费者必须等待;2)缓冲区满时,生产者必须等待。

    然后每个约束用一个单独的信号量。
    互斥约束用一个二进制信号量mutex。
    两个同步约束分别用两个一般信号量fullBuffers和emptyBuffers

    注意:mutex的位置一定要在获取emptyBuffer和fullBuffer之后,否则会死锁。
    (2)用管程(锁+条件变量)解决

     

    8. 总结

  • 相关阅读:
    Http请求头和相应头分析
    linux扩充容量经典篇
    Redis持久化以及其原理
    redis简单应用
    Python Requests库使用2:请求方法
    加快访问GitHub的速度
    GET和POST两种基本请求方法的区别
    Python Requests库介绍
    Python urllib、urllib2、urllib3用法及区别
    Django中操作cookie和session
  • 原文地址:https://www.cnblogs.com/tsiangleo/p/4902294.html
Copyright © 2011-2022 走看看