zoukankan      html  css  js  c++  java
  • 并发实现机制-2-互斥实现

    并发核心问题是对资源的互斥需求,互斥的实现有多种方法(硬件方法和软件方法)

    互斥的方法

    1. 专用机器指令,较少开销但不通用。(硬件支持)
    2. 操作系统或程序设计语言提供某种级别的支持。(软件方法:操作系统和程序设计语言的支持)
    3. 进程承当实现互斥的责任,这是软件方法,增加了开销并存在缺陷。(软件方法:进程承担责任)

    其中方法1的几种实现有:中断禁用、专用机器指令;2的几种方法有:信号量、管程、消息传递等;方法3的几种实现有Dekker算法、Peterson算法、竞争条件和信号量等。这几种方法的详细说明将在本篇文章总结。

    硬件支持

    硬件实现的几种方法有:中断禁用,专用机器指令

    中断禁用:只需要保证一个进程不被中断即可,通过内核提供启用中断和禁用中断的原语来支持,但是代价很高,执行的效率会明显降低,另外不适用于多处理器

    专用机器指令:compare and swap指令,用一个测试值(testval)检查一个内存单元(*word)。如果这个内存单元的当前值是测试值(testval),就用新的值(newval)替换该值,否则保持不变。(当word为0可以进入临界区,出临界区把word重置为0,如果当前有一个进程在访问临界区,其他进程的testval是1,compare and swap返回的是1,如果没有任何进程在临界区,testval为0,compare and swap返回的是0,如果检查到返回的值和传入的testval不相等,说明word的值在上一次循环被更改),当有进程处于临界区时,其他进程处于忙等待(自旋等待),

    机器指令只解决了互斥,可能饥饿(选择进入临界区的进程是任意的)和死锁(如果P1在临界区被中断、并且P1在临界区正在持有一个资源R的访问,那么如果P2此时被调度执行并且试图访问R时就会失败,如果P2比P1的优先级高,P1永远不会被执行,P2永远处于忙等待,陷入死锁)

    软件方法:操作系统/程序语言支持

    todo: 表5.3 一般常用的并发机制截图

    信号量 semaphore

    二元信号量 binary semaphore,值只能是0或者1

    struct binary_semaphore{
    	enum {zero,one} value;
        queueType queue;
    }
    void semWaitB(binary_semaphore s){
        if(s.value==one){
            s.value = zero;
        }else{
            /*把当前进程插入队列*/
            /*阻塞当前进程*/       
        }
    }
    void semSignalB(binary_semaphore s){
        if(s.queue is empty()){
            s.value = one;
        }else{
            /*把进程P从队列移除*/
            /*把进程P插入就绪队列*/       
        }
    }
    

    计数信号量counting semaphore(一般信号量 general semaphore)

    struct semaphore{
        int count;
        queueType queue;
    }
    void semWait(semaphore s){
        s.count--;
        if(s.count<0){
            /*把当前进程插入队列*/
            /*阻塞当前进程*/
        }
    }
    void semSignal(semaphore s){
        s.count++;
        if(s.count<=0){
            /*把进程P从队列移除*/
            /*把进程P插入就绪队列*/
        }
    }
    

    信号量解决互斥问题的写法如下

    const int n= /*进程数*/;
    semaphore s = 1;
    void p(int i){
        while(true){
            semWait(s);
            /*临界区*/
            semSiganl(s);
            /*其他部分*/
        }
    }
    void main(){
        parbegin(P(1),P(1),...,P(n));
    }
    

    信号量的实现

    semWait和semSignal的实现必须是原子原语,任何时候只有一个进程可用semWait或者semSignal操作控制一个信号量。

    可以使用软件方案:dekker算法或者peterson算法,但这有处理开销

    可以使用硬件方案:comapare&swap和中断禁用(单处理器系统),因为semWait和semSignal的操作时间很短,因此忙等待或者中断禁用的时间都分长短,是比较合理的。

    实现方法如下(注意important语句,并注意堵塞进程时的处理,因为堵塞进程时进程无法到达最后一句,所以堵塞时就应该执行flag置为0或者允许中断!!!)

    void semWait(semaphore s){
        while(compare and swap(s.flag,0,1)==1); // important ! 或: 中断禁用
        s.count--;
        if(s.count<0){
            /*把当前进程插入队列*/
            /*阻塞当前进程,并设置flag为0或允许中断*/ // important !
        }
        s.flag=0; // important ! 或: 中断允许
    }
    void semSignal(semaphore s){
        while(compare and swap(s.flag,0,1)==1); // important !
        s.count++;
        if(s.count<=0){
            /*把进程P从队列移除*/
            /*把进程P插入就绪队列*/
        }
        s.flag=0; // important !
    }
    
    • PV操作: P操作用于semWait V操作用于semSignal

    • 进程按照什么顺序从队列中移除?FIFO最公平,被堵塞最久的进程最先从队列释放,采用这一策略的信号量是强信号量,没有规定队列移出顺序的信号量为弱信号量。强信号量确保了不会饥饿。

    • 二元信号量与互斥锁(mutex)的区别:为互斥量加锁的进程和解锁的进程必须为同一进程,而二元信号量可以由一个进程加锁,而另一个进程解锁

    • 信号量本质上处理的是进程进入和移出 阻塞和就绪队列

    • 信号量一般初始化为1,这样第一个执行semWait(s)的进程可以立即进入临界区,并把s的值置为0,接下来任何试图进入临界区的其他进程都会被堵塞。

      程序也可以公平的处理一次允许多个进程进入临界区的需求,这个需求可以把信号量初始化为某个特定的值来实现。无论何时s.count的值可以解释如下

      (count大于1的值的情形例子如哲学家就餐问题,虽然count大于1不能保证临界区互斥,但是没有告诉我们不能再临界区中再加一个信号量来保证互斥,外层count大于1的信号量用于限制临界区进程数量,第二个count等于1的内层信号量用于保证互斥)

      • s.count>=0:s.count是可以执行semWait(s)而不被堵塞的进程数。这种情形允许信号量支持同步和互斥。
      • s.count<0: s.count的大小是堵塞在s.queue队列中的进程数

    管程

    使用信号量设计程序是困难的,难点在于semWait和semSignal操作可能分布在整个程序中,而且很难看出信号量在这些操作上产生的整体效果。

    管程是一种结构,可以用管程来锁定对象。

    管程是由一个或多个过程(函数、方法)、一个初始化序列和局部数据组成的软件模块。(更加面向对象),管程特点如下

    • 局部数据变量只能被管程的过程访问
    • 只能调用管程的一个过程来进入管程
    • 在任何时间,只能有一个进程在管程中执行,调用管程的任何其他进程都被堵塞,

    进程不仅能够在管程中被堵塞,也能够释放这个管程,当条件满足且管程再次可用时,需要恢复该进程并允许它在堵塞点重新进入管程。

    管程使用条件变量来支持同步,cwait(c),csignal(c),如果管程中的一个进程发信号,但是没有在这个条件变量c上等待的任务,那么丢弃这个信号

    管程优于信号量的地方在于,所有的同步机制都被限制在管程内部,因此不但易于验证同步的正确性,而且易于检测出错误????

    • 使用信号的管程 ??? 未

    • 使用通知广播的管程 ??? 未

    消息传递

    xx 未

    和socket的关系???

  • 相关阅读:
    BZOJ 4802 欧拉函数(Pollard_Rho)
    Codeforces 804E The same permutation(构造)
    Codeforces 804D Expected diameter of a tree(树形DP+期望)
    BZOJ 3399 [Usaco2009 Mar]Sand Castle城堡(贪心)
    BZOJ 2430 [Poi2003]Chocolate(贪心+归并排序)
    BZOJ 1707 [Usaco2007 Nov]tanning分配防晒霜(扫描线+贪心+优先队列)
    BZOJ 1828 [Usaco2010 Mar]balloc 农场分配(贪心+线段树)
    BZOJ 1827 [Usaco2010 Mar]gather 奶牛大集会(树形DP)
    BZOJ 2697 特技飞行(贪心)
    BZOJ 4883 [Lydsy2017年5月月赛]棋盘上的守卫(最小生成环套树森林)
  • 原文地址:https://www.cnblogs.com/cheaptalk/p/12549665.html
Copyright © 2011-2022 走看看