精华内容
下载资源
问答
  • 临界资源的同步与互斥,区分临界资源与临界区,二义性分析
    千次阅读
    2020-02-17 16:22:47

    描述

    互斥:同一时间,当只保证互斥,则可以保证临界资源访问不会造成临界资源数据的二义性,但是有可能占有临界资源的进程一直在占有,导致后面进程访问等待时间比较长
    同步:保证对临界资源访问的合理性

    1. 互斥
    如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入,保证只能有一个进程访问临界资源
    任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待
    2. 同步
    进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区
    如果进程不能进入自己的临界区,则应让出 CPU,避免进程出现 “ 忙等 ” 现象
    

    以管道举例:管道也要满足同步互斥的原则

    当写端往管道当中写入数据的时候不超过 PIPE_BUF(一次性可读大小最大为4k,PIPE_SIZE管道大小为64k) 的时候,管道保证进程在读取数据时的原子性

    概念

    1. 原子性:进程当前的操作不能被打断,即对管道的读写操作不能被打断,由于此特点,导致操作结果只有一个,要么完成,要么未完成,体现互斥同步思想

    2. 临界区:每个进程中访问临界资源(比如全局变量等公用资源)的代码称为临界区

    3. 临界资源:临界资源是一次仅允许一个进程使用的共享资源,如全局变量等),也称为临界段或互斥资源

    二义性

    每个进程都可以访问这个全局变量的代码,对于某一临界资源,对应的临界区具体来说有多个(多个进程都可以有)。

    同一时间,当前的资源只能被一个进程所访问,由于不同的线程(进程),对资源的访问(读,写,修改),可能造成数据的二义性
    用户对临界资源的访问 + 操作系统对进程的调度,有可能导致修改操作不是一步完成的,中途操作被打断,这会儿就有可能有其他的进程访问到上一个进程需要修改的资源,就会造成修改资源之后,数据的二义性,从而导致程序运行结果不正确
    举个例:
    有两个PCB(p1,p2)分别要对一个全局变量 val = 10 进行 – 的操作,正常情况下结果应该是 8 ,但在临界资源的访问 + 操作系统对进程的调度下,可能会导致 p1 先将数据进行寄存未修改,p2 就已经占用 CPU 进行数据寄存和修改 val 为 9,之后再进行 p1 的修改,p1根据上下文信息将未修改前的 val (10)进行修改,依旧得到 9

    更多相关内容
  • 解决死锁问题,也就是旧进程资源不释放,新进程得不到资源。 什么是银行家算法 银行家就理解成放贷的人,放贷的人,是需要通过放贷赚钱的,所以他必须准确的把握放出去的钱能不能收回来,因为众所周知,有很多老赖,...

    银行家算法的目的

    解决死锁问题,也就是旧进程资源不释放,新进程得不到资源。

    什么是银行家算法

    银行家就理解成放贷的人,放贷的人,是需要通过放贷赚钱的,所以他必须准确的把握放出去的钱能不能收回来,因为众所周知,有很多老赖,拖着钱不还,甚至自杀,携款潜逃,所以放贷的人必须确保钱能收回盈利,因为银行家自己的钱也是有限的,他要有资源,给更多对他有利的人放贷。操作系统就是一个银行家,它有很多资源,也有很多进程向他申请资源,所以操作系统必须管控好这些资源,避免造成死锁。

    银行家算法的具体规则

    1.当一个进程对资源的最大需求量不超过系统中的资源数时可以接纳进该进程
    2.进程可以分期请求资源,但请求的总数不能超过最大需求量
    3.当系统现有的资源不能满足进程尚需资源数时,对进程的请求可以推迟分配,但总能使进程在有限的时间内得到资源

    具体例子:
    假设系统中有三类互斥资源R1 R2 R3 可用资源分别是9,8,5.在T0时刻系统中有P1,P2,P3,P4和P5五个进程,这些进程对资源的最大需求量和已分配资源数如下所示,如果进程按 序列执行,那么系统状态是绝对安全。

    这个很好算,在图片右侧有已经分配好的资源数,所以我们得先计算,系统还剩下多少资源。
    R1: 9-1-2-2-1-1=2
    R2: 8-2-1-1-2-1=1
    R3: 5-1-1-3=0

    而P1还缺 5个R1 3 R2 1R3 A选项排除
    P2还缺 1个R2 可先走R2
    P4还缺 1个R3 但是R3已经没了 所以D排除

    所以先走P2 P2执行完空闲资源变成
    R1:4 R2:3 R3:1 此时走P1以及资源不足,所以C排除,答案选B

    在这里插入图片描述

    展开全文
  • 1.前驱图 前驱图,用节点表示操作,可以理解成一个进程。节点间的箭头线,代表两个节点(进程)的先后...独占系统资源,在操作系统中,进程是占有资源的最小单位(线程可以访问其所在进程内的所有资源,但线程本身并

    1.前驱图

    前驱图,用节点表示操作,可以理解成一个进程。节点间的箭头线,代表两个节点(进程)的先后关系(同步)

    前驱图的作用,用于描述进程之间的关系。我们后面会经常用到前驱图这个工具。

    2.单道环境下,程序运行的特征

    1. 资源独占性 (封闭性)
      任何时候,位于内存中的程序可以使用系统中的一切资源,不可能有其他程序与之竞争;
      封闭性指的是,程序的运行,在一个封闭的环境下运行的,不会被别的程序干扰。只有一个进程。独占系统资源,在操作系统中,进程是占有资源的最小单位(线程可以访问其所在进程内的所有资源,但线程本身并不占有资源或仅仅占有一点必须资源)。
    2. 执行的顺序性
      内存中只有一个程序,各个程序是按次序执行的。在做完一个程序的过程中,不可能夹杂进另一个程序执行;
    3. 结果的可再现性
      只要执行环境和初始条件相同,重复执行一个程序,获得的结果总是一样的;
      一个正确的程序,逻辑上,应该是这样。
      如果我们输入的初始条件一样,那么结果应该是一样的。
      比如,加法程序。无论我们运行多少次,只要初始条件是1+1,那么结果应该就是等于二,这就是可再现的含义,否则就称为不可再现。
    4. 运行结果的无关性
      程序的运行结果与程序执行的速度无关。系统中的作业以串行的方式被处理,无法提高CPU、内存的利用率;

    3.程序并发执行的特征,待解决的问题

    ①首先理解什么是并发运行:
    用自己的话来说,并发执行就是,多个程序一起进入内容,然后被等待调度执行,某一个时刻,只有其中一个进程占用cpu----真正的运行,但是,给用户的感觉,好像这一组程序是一起运行的(有时间间隔,宏观上同时进行的)。并发,就是同一个时间段(时间间隔)内发生的。

    ②多道程序一起进入内存运行,会出现什么问题呢?也就是,并发执行的时候,会有什么特征?
    1.间断性:程序运行呈现出间断的特征。比如说,一个程序运行,然后需要输入输出,这时,就要让出cpu给别的进程,等完成输入输出,再继续等待被调度运行。整个运行中,走走停停,
    程序并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致使在这些并发执行的程序之间形成了互相制约的关系:并发程序具有“执行–暂停–执行”的间断性的活动规律。
    2.失去封闭性 根本原因,就是多道程序,共享系统资源,导致存在资源竞争的问题,所以失去了封闭性,就是一个程序,会被别的进程影响。当系统中存在着多个可以并发执行的程序时,系统中的各种资源将为他们共享,而这些资源的状态也由这些程序来改变,致使其中任…
    不可再现性: 因为第二个特征,就会导致第三个特征。两个循环程序A和B,它们共享一个变量N。
    在这里插入图片描述
    通过这个例子来体会,为啥会失去可再现性?研究了,并发执行可能会出现不可再现的情况;总结了,根本原因就是并发进程对共享资源的同时访问。所以,解决不可再现问题的关键在于,对共享资源,不允许并发进程同时访问。
    A,B两个进程,并发执行,也就是这两个程序一起进入内存,根据异步执行的特征,操作系统,可能先调度A运行,再调度B,也可能先调度b先运行,再调度A,也有可能,A执行的过程中,可能因为什么原因停下来了,比如,时间片用完了,它必须让出cpu。也就是执行了一半,停下来,又执行B,B执行完,重新调度A,继续运行,所以A、B并发执行,存在几种情况。
    ①第一种情况。操作系统先调度A进程,再调度B
    也就是先执行A里面的,N=N+1.
    在执行B语句,print N,N=0;
    这种调度情况,执行结果为

    ②第二种情况,操作系统先调度B,再调度A运行。
    也就是先执行 print N,N=0,在执行A里面的,N=N+1;
    这种调度情况,执行结果为
    ③第三种情况,先调度B运行,运行到一半,可能是因为时间片等原因,B让出cpu,调度A运行,A运行完后,在调度B继续运行这种情况,运行结果为也就是说,A,B两个进程,一起进入内存运行。可能的结果有三种,这就是不可再现。
    不可再现肯定是不行的,比如,1+1,逻辑上,结果永远都是2,所以,不可再现的问题,是我们并发执行要解决的问题,必须可再现,要解决这个问题,要搞清楚原因,什么导致的不可再现?
    根本原因在于资源的共享,我们上面的这个例子,共享的资源,就是变量N对共享资源的同时访问导致程序的不可再现,所以,解决这个问题的关键在于:不允许并发进程同时访问共享资源,也就是资源的互斥访问,就是解决这一个问题的,这种互斥资源,现实中很多,比如说大学的大部分教室,是全校共享的,但是某一个时刻,只能一个班使用,否则就出问题了;再比如,铁路也可能是共享的,但是,同一个时刻只能一列火车运行,否则,问题就严重了。讲了这么多。就是通过这个例子来理解,为什么并发执行会出现不可再现。

    4.进程

    进程的概念,主要体现运行这个特征,进程是有结构的,由程序、数据以及进程控制块(PCB),三个部分组成,进程控制块,进程所有的信息,都记录在进程控制块PCB中的,它是操作系统感知进程的唯一标识,所以这个数据结构很重要,一个进程对应一个PCB,创建进行的时候,有 一个很重要的工作,就是分配空白的PCB,然后对PCB初始化,其他特征就不解释了,并发,异步,前面解释过了。
    在这里插入图片描述

    这个图,关键的地方,描述了,进程状态是如何切换的,比如,在这里插入图片描述
    阻塞没有指向执行的箭头,说明,不可能从阻塞直接切换到运行,进程的运行,是由操作系统根据当时的情况调度运行的,所以,阻塞完成后,只能切换到就绪,等待。进程运行过程中,可能因为要等待某个事件的发生,就会进入阻塞状态。典型的情况就是等待输入输出(请求I/O)。阻塞状态,当等待的事件发生,不会直接切换到运行,而是进入就绪,等待再次被调度运行。

    我们通过前面的例子,明白了,对临界资源的同时访问,导致并发执行的程序出现不可再现的问题。解决这个问题的关键在于对临界资源应该互斥访问。现代操作系统普遍采用信号量机制来实现进程的互斥访问临界资源;利用信号量来实现进程间的同步。

    5.利用信号量机制解决资源的互斥访问

    一、
    如果多个进程间存在时序关系,需要协同工作以完成一项任务,则成为同步(如输入进程和计算进程);如果不满足协同的条件,而知识因为共享具有排他性资源时所产生的关系称为互斥(如A、B两个进程同时申请同一台打印机)
    用信号量实现互斥,互斥是并发进程之间由于共享资源而形成的间接制约关系,互斥需要调解两者使用资源的速度;
    先讨论进程间的两种制约关系:间接制约,直接制约;
    间接制约,是因为资源共享导致的,比如,教室全校都可以用,但是,如果有别的班级在用,我们就必须等,被制约了;
    第二种制约关系,直接制约,这里,就是我们前面讲的同步问题,也就是说,并发进程之间存在着先后关系,比如说,c语言学习就是数据结构(C语言编写版)学习的前驱,数据结构为后继,这个事情,顺序不能错,这就是直接制约,其实就是同步问题;

    这里,提了几个概念,解释一下:
    1.临界资源:不允许同时访问的,叫临界资源;
    2.临界区:访问临界资源的那段代码,叫临界区,对临界资源的同时访问,导致了不可再现的问题
    二、
    下面,我们来研究怎么解决临界资源的同时访问的问题
    这个问题,现实中有很多例子,比如,最容易想到的办法就是加锁,比如商场里公用的放物品的箱子(存储柜),如果没人用,那我就可以用,然后上锁,别人就不能用了,用完之后,再解锁,别人就可以用了,这个是解决同时访问的常见方法。所以,我们写程序,访问临界资源的代码,通常是,先看看有别的进程在临界区吗?没有人用的话,先上锁,再访问临界资源。访问完,记得解锁,不然,就永远被锁了,大家都用不了,所以,加锁解锁都是成对出现的。
    在这里插入图片描述
    就是这种格式,进入的工作,通常包含检查,加锁等,退出的工作,主要是解锁,这样就避免了同时访问临界资源的问题,就不会出现不可再现现象。提醒下自己:
    既然写下了这个问题,以后对临界资源要非常敏感,访问共享资源了,条件反射似的要考虑到同时访问的问题,不然,程序肯定会出错了,现代操作系统普遍采用信号量机制来实现互斥、同步问题。
    用信号量来解决互斥访问问题,得先学习信号量的概念

    整型信号量:

    最初由Dijkstra把信号量定义为一个整型量,除初始化外,仅能通过两个标准的原子操作wait(S)和signal(S)来访问。这两个操作一直被分别称为P、V操作。
    仔细看这个定义,信号量和普通整形变量的区别在哪?
    信号量的含义,通常用于表示资源的数目,跟普通整型量最大的区别在于,不可以对信号量直接访问,只能通过,不允许直接访问、操作信号量,只能用两个标准的原子操作来进行,wait(s)、signal(s)。
    先来研究wait(s)的内部代码,
    在这里插入图片描述
    一个循环,如果信号量小于等于0,就退不出循环,被阻塞在这,这个条件,可以看到,信号量小于等于0,说明没有资源,被阻塞等待,一直到条件成立,条件成立,那信号量大于0,说明有资源了,可以从循环退出,往下运行,里面的操作,就是把信号量减一,相当于申请到一个资源,资源少了一个,这个是wait操作的内部情况,我们可以把wait操作理解成,申请资源,没有资源,就被阻塞等待,本质是在while循环那里,空转,有资源,那么资源数减一,可以理解成加锁。
    在这里插入图片描述
    可以理解成释放资源,里面代码,将资源数加一,这一对操作很重要,要重视。
    熟练利用信号量解决互斥问题的步骤:
    在这里插入图片描述
    写程序的时候,要敏感,哪些是互斥资源,如果是互斥资源,一定要确保互斥访问,具体方法,一类资源,定义一个互斥信号量,初值设为1,访问资源之前,对互斥信号量做一次wait操作,访问完,做一次signal操作,注意wait和signal成对出现。
    看一下下面的图片(思考题可不用关注,也不完整。)
    在这里插入图片描述
    解决进程互斥,可以按上面的例子来写,基本都一样,在这里插入图片描述
    前面做一次wait操作访问临界资源,访问完做一次signal,相当于释放资源,不然,就永远被锁住了,别的进程进不了临界区。
    如果我们按照这个套路写,有没有可能出现并发进程同时进入临界区的情况?
    在这里插入图片描述
    这个例子,两个并发进程,有没有可能两个进程同时进入临界区呢?实际上,是不可能的,比如,操作系统,先调度p1(process1简写,表示进程1,下同),p1进入临界区前,首先对互斥信号量做wait(上锁)操作,mutex就变为0了,假设,这时p2想进入,实际上是进入不了的,因为,首先要做wait操作,mutex=0,无法从wait操作返回,被阻塞到wait这里等待,不能进入临界区,也就不会对互斥资源进行同时访问。所以,只要是临界资源,我们就按这个套路写:
    第一步,设置互斥信号量,初值为1;第二步,进入前,对互斥信号量做一次wait;第三,出临界区的时候记得做一次signal,确保wait和signal成对出现,这样就能确保,对临界资源的互斥访问,也就解决了,并发程序执行的不可在现的问题。

    通过学习可以知道,除了整型信号量,还有记录型信号量,为什么有了简单信号量,还要弄一个记录型信号量?因为简单信号量,违背了同步的几个原则的其中一个。先上同步机制应遵循的规则:
    1.空闲则入:其他进程均不处于临界区;
    2.忙则等待:已有进程处于其他临界区;
    3.有限等待:等待进入临界区的进程不能“死等”;
    4.让权等待:不能进入临界区的进程,应释放CPU。
    主要违背了第一条:空闲则入:其他进程均不处于临界区;什么意思?也就是一个进程不能进入临界区的时候,要让出cpu,不能占着cpu什么都不干。
    我们来仔细研究一下:
    在这里插入图片描述
    当S小于等于0,实际上,一直在做检测工作, 在while循环那里空转,别的进程想运行,就不行,违背了上述原则,这种写程序的方式也不好,举一个之前的例子,比如,我们要开会,要去教室开,教室是共享资源(了解大学的特点都懂),我们一去,发现有人在用,就只能在那里等。。。这浪费了我们宝贵的时间,本来,我们可以干很多别的事情按这种写法,就是不停的看,教室是不是空闲了?干这事去了,实际上,一个比较好的方法是,我们去申请教室,如果有人用,那么,我可以先登记一下班级信息,说明我们要用,等别人用完了,发一个信号过来,告诉所有等待这个教室的班级,然后,大家继续抢教室,这种的好处是,不用总是检测,我们可以干别的事情,收到空闲通知,我们再去抢,这里道理是一样的。
    提出了第二个信号量,
    在这里插入图片描述
    有两个数据项,value,相当于原来的s资源数,不同的地方在于多了一个链表,L:list of process; 这个链表干嘛用的?把所有申请该资源的进程串在一个链表里。这样的好处在于,等资源空闲了,只需要给这个链表的所有进程发一个信息,然后,这组进程就可以继续抢资源。
    在这里插入图片描述
    看wait操作,有什么不同:先减一,先申请,
    在这里插入图片描述
    如果资源小于0,那么意味着资源被别人抢走,那么,就把申请该资源的进程串链表里,可以干别的事情,等通知,大家对比一下简单信号量的wait,前者,是不断的检测,这里是,相当于申请了,就干别的事情,等通知,解决了让权等待问题。

    这里提一下S.value的含义:
    1.
    在这里插入图片描述
    2.
    在这里插入图片描述
    下面,认识第三种类型的信号量,AND型信号量,为什么提出第三种类型的信号量,因为,实际中,申请的资源类型往往不是一种,而是多种,为了方便而提出。这种信号量的思想:在这里插入图片描述
    为什么会有这种思想,这个思想,最重要的一条,“要么全部分配到进程,要么一个也不分配”,为了帮助理解,举一个例子,假设有两个并发执行的进程,A,B,这两个进程,都需要申请资源D,E,那么,进程可能是这样:在这里插入图片描述
    先看进程A(process A,pA,下同),两个wait操作,相当于进程A,先申请D资源,在申请E资源,相当于进程B,先申请E资源,在申请D资源,当pA,pB两个进程并发执行时,会出现一种极端情况,比如,操作系统,先调度A运行,会出现一种极端情况,
    在这里插入图片描述
    当执行完第一条语句,可能因为时间片等原因停下来了,这时,操作系统会调度B运行,如果出现这种调度情况,那么死锁产生了,当前这种状态相当于A进程占有了D资源,B进程占有了E资源,然后,都希望继续占有另外一部分,比如A还想占有E,这时E资源被B进程占用,申请不成功,被阻塞,同理,B进程申请D资源,被A进程占用,申请不成功,被阻塞,两个进程,这时候无法进行下去了,处于僵持状态,换句话说,两个进程永远都运行不完了,那当然不行了,我们通过这个例子总结一下:当死锁发生的时候,并发进程,都占用了一部分资源,同时还要申请对方所占用的另外一部分,也就是死锁发生的时候,通常都是占有了一部分资源,还需要别人占有的另外一部分,其中,一个很容易想到不产生死锁的办法,就是不允许部分分配,这就是AND型信号量的思想。
    在这里插入图片描述
    这样就可以确保不产生死锁现象,看起来,可以解决问题,但是这种方法有一个不好的地方,按照这个思想,如果有一个资源不满足,就不分配,那么可能会导致某些进程长期得不到运行,而且重要的是,资源利用很低,可行,但缺点比较严重,早期的学者也提出了很多改进。

    第四种信号量类型,信号量集,为什么要提出这种类型的信号量?实际中,往往申请的资源数都是多个,前面提到的信号量的wait方法都是一个一个申请,比较麻烦,第二,执行多次wait容易产生死锁,所以提出第四种信号量,跟前面最大的区别就在于可以一次申请多个资源,有特殊的情况。

    总结:
    信号量解决资源的互斥访问,因为对临界资源同时访问,会照成不可再现的情况。所以,对临界资源,并发进程必须互斥访问,才能确保运行的可再现。利用信号量解决互斥访问。
    我们只需要,
    1.识别临界资源,设置互斥信号量,初值设为1
    2.进入临界区之前,做一次wait(P)操作
    3.退出临界区时,做一次signal(V)操作
    就能确保并发进程对临界资源是互斥访问的

    6.利用信号量机制解决同步问题

    同步,就是直接制约关系。换一句话说,就是并发执行的进程之间存在着前驱后继关系。比如,我们前面举的例子。学习c语言,就是学习数据结构(C语言编写版)的前驱,学习这个事情要顺利进行,必须确保,学C语言先完成。数据结构的学习必须在学C语言之后(C语言学习就是数据结构(C语言编写版)学习的前驱,数据结构为后继,这个事情,顺序不能错,这就是直接制约,其实就是同步问题)。现在要解决的问题是,两件事情,也就是两个进程,一起进入内存,并发执行。如何确保前驱先执行,后继后执行呢?也就是这里讲的,如何实现同步。前驱图是用于描述进程之间的关系的,我们可以通过前驱图,来了解进程间的同步关系,前驱图中,一条边对应着一个同步关系。我们必须确保所有的进程,按同步关系来运行。在这里插入图片描述
    如上图这个例子,有六个节点,代表有六个进程,进程之间的关系,看箭头,比如,我们这个图,s1是s2与s3的前驱,s2,s3必须在s1运行完了,才能运行,六个进程一起进入内存,并发执行,如何确保运行,按图中的关系运行,第一步,画出前驱图,第二步,途中一条边对应一个同步关系,为每一条边设置一个同步信号量,初值设为0。我们这个图有七条边,说明存在七个同步关系,所以需要定义,七个同步信号量,初值设置为0。第三步,实现同步关系。只需在前驱的后面加上signal语句,后继的前面加一条wait语句。
    在这里插入图片描述
    我们看这部分,s1是前驱,s2,s3是后继,则进程s1的代码如下:
    在这里插入图片描述
    看这段代码:
    在这里插入图片描述
    定义了七个信号量,初值为0,因为图中有七条边,有七个同步关系,接下来看这段:
    在这里插入图片描述
    看s1的代码,按套路,前驱的后面,对同步信号量做signal操作,因为,s1是s2,s3的前驱,所以在这里插入图片描述
    s1语句的后面,做了两次signal,我们再看s2的代码,在这里插入图片描述
    按套路,后继的前面对同步信号量做wait操作,所以,一开始就是wait(a),signal(c);signal(d),这个是因为,s2是后面两个进程的前驱依次完成一样的,就不多说了。现在我们看一下,如果按这个套路来写,有没有可能后继先运行,p1和p2一起进入内存,操作系统有可能先调度p2,在这里插入图片描述
    这个是p2,p2一运行,就会执行第一条语句wait a,a的初值为0,实际上,一开始调度p2运行的话,就会被阻塞在wait操作这里,无法运行后继的语句,必须等,只有等前驱p1,前驱运行完后,对a做了signal操作,p2才能从wait返回,执行后继语句,从这里可以看出,只要按这个套路来写,就能确保:前驱先运行,后继后运行,从而也就实现了同步运行。

    PS:由于本人水平有限,错误之处还请指正,不胜感激。

    展开全文
  • 文章目录多线程间的资源竞争互斥互斥锁示例可重入锁与不可重入锁死锁 多线程间的资源竞争 以下列task1(),task2()两个函数为例,分别将对全局变量num加一重复一千万次循环(数据大一些,太小的话执行太快,达不到...

    多线程间的资源竞争

    以下列task1(),task2()两个函数为例,分别将对全局变量num加一重复一千万次循环(数据大一些,太小的话执行太快,达不到验证的效果)。

    import threading
    import time
    
    num = 0
    
    
    def task1(nums):
        global num
        for i in range(nums):
            num += 1
    
        print("task1---num=%d" % num)
    
    
    def task2(nums):
        global num
        for i in range(nums):
            num += 1
        print("task2---num=%d" % num)
    
    
    if __name__ == '__main__':
        nums = 10000000
        t1 = threading.Thread(target=task1, args=(nums,))
        t2 = threading.Thread(target=task2, args=(nums,))
    
        t1.start()
        t2.start()
        # 因为主线程不会等子线程执行完就会执行,所以这里延迟五秒,确保最后执行。
        time.sleep(5)
        print("main----num=%d" % num)
    

    程序运行结果:
    在这里插入图片描述
    如图,输出结果比较混乱,既没有一千万,最终结果也不是二千万。因为多线程运行时出现了资源竞争,即可以理解为,每个函数运行的时间都不确定,且互相影响,
    如从初始值0开始,假设t1的线程先执行,执行到+1后,此时的num=1还未存储,然后即被叫停,t2开始执行,去获取num,获取到的num等于初始值0,然后其执行了+1并存储,存储后num=1,然后t2停止t1继续,再次存储num=1。即加了两次1,但是num还是只等于1。
    因为t1和t2谁来运行的分配是完全随机的,所以最后加了两千万次1后值是小于2000万的。

    解决此类问题,可以使用到互斥锁


    互斥锁

    • 某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变,只到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。
    • 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

    互斥锁示例

    创建一把锁:

    mutex = threading.Lock()

    mutex.acquire() # 上锁
    xxxx锁定的内容xxxxx
    mutex.release() # 解锁

    将互斥锁加入到上边的代码中如下,则问题得到了解决。

    import threading
    import time
    
    num = 0
    
    
    def task1(nums):
        global num
        mutex.acquire()
        for i in range(nums):
            num += 1
        mutex.release()
        print("task1---num=%d" % num)
    
    
    def task2(nums):
        global num
        mutex.acquire()
        for i in range(nums):
            num += 1
        mutex.release()
        print("task2---num=%d" % num)
    
    
    if __name__ == '__main__':
        nums = 10000000
        mutex = threading.Lock()
        t1 = threading.Thread(target=task1, args=(nums,))
        t2 = threading.Thread(target=task2, args=(nums,))
    
        t1.start()
        t2.start()
        # 因为主线程不会等子线程执行完就会执行,所以这里延迟五秒,确保最后执行。
        time.sleep(5)
        print("main----num=%d" % num)
    

    程序运行结果:
    在这里插入图片描述

    可重入锁与不可重入锁

    threading.Lock()上的是不可重入锁,即一次只能加一把锁,不能加多把。

    threading.Lock()

    如果需要同时加多把所,则需加入不可重入锁

    创建一把可重入锁:

    mutex = threading.RLock()
    mutex.acquire() # 上锁
    mutex.acquire() # 再上锁
    xxxx锁定的内容xxxxx
    mutex.release() # 解锁
    mutex.release() # 再解锁

    其中上锁和解锁的次数必须保持一致。


    死锁

    在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会程序堵塞,造成死锁。

    • 死锁一般用不到。
    • 程序设计要尽量避免。
    展开全文
  • 临界区的互斥控制_SetEvent置句柄为有信号状态配合WaitForSingleObject使用_INFINITE等待其运行结束
  • 进程间互斥访问临界资源

    千次阅读 2019-07-11 03:42:37
    进程间互斥访问临界资源 1、使用临界区变量 HANDL g_hMutex; HANDLE g_hThread[2]; main() { g_hMutex=CreateMutex(NULL,FALSE,NULL); g_hThread[0]=CreateThread(..); g_hThread[1]=CreateThread(..); } ...
  • 互斥是并发进程之间由于共享资源而形成的间接制约关系。 Var mutex : semaphore := 1; //初值为1 Begin Parbegin(由部分开始) Process1: begin repeat(循环) wait(mutex);//进入区(P操作) critical ...
  • 互斥 互斥的解决方案

    2021-01-05 20:13:56
    互斥的作用:保护数据的完整性 进程间通信会引起死锁,(两个都等互相发消息过来),可能会有饥饿现象 互斥的要求: 一次只允许一个资源去使用临界起源 在非临界区终止了这个进程的执行,也不能影响其他进程 不允许...
  • 进程互斥 进程互斥:在多个程序中,有两个进程不可以同时进行(例如读,写操作)。...如果操作系统将资源分配给其中的某一个进程使用,另一个进程就必须等待,直到申请的资源可用时,由操作系统分配给他们。 ...
  • 进程互斥:当一个进程在访问临界资源时,另一个想要访问该临界资源的进程必须等待。当前访问临界资源的进程访问结束,释放该资源之后,另一个进程才能访问该临界资源 二.进程互斥的组成部分 进入区:用于检查是否...
  • Python写的多线程使用互斥锁解决资源竞争的问题的代码,可以直接运行,并且带中文注释,方便初学者学习和使用。
  • 操作系统课的一个小作业,使用C#处理多线程的同步与互斥,使用Mutex类和AutoResetEvent类。作为一个小白,参考了其他代码写出来,某些地方还有些不完全明白,都写在注释里了,求高手指点。
  • vc++中使用临界区CriticalSection的例子.zip
  • 互斥与同步的概念 在多道程序环境下,系统中可能有许多并发的进程,在这些进程之间存在以下两种关系:间接相互制约关系、直接相互制约关系。 间接相互制约关系 多个进程彼此无关,它们并不知道其他进程的存在。由...
  • 任务 Task1 运行的过程需要调用函数 printf,发现任务 ... 任务 Task2 执行完毕后,任务 Task3 恢复执行,Task3 释放互斥资源后,任务 Task1 得到互斥资源,从而可以继续执行。上面就是一 个产生优先级翻转问题的现象。
  • 临界资源/临界区/互斥

    千次阅读 2017-12-25 09:39:25
    临界资源: 多道程序系统中存在许多...各进程采取互斥的方式,实现共享的资源称作临界资源。 属于临界资源的硬件有打印机、磁带机等,软件有消息缓冲队列、变量、数组、缓冲区等。 诸进程间应采取互斥方式,实现
  • 进程的同步和互斥

    2021-06-05 20:52:11
      两个或两个以上的进程,不能同时进入关于同一组共享变量的临界区域,否则可能发生与时间有关的错误,这种现象被称作进程互斥· 也就是说,一个进程正在访问临界资源,另一个要访问该资源的进程必须等待。...
  • 实验目的:通过编写程序实现进程的同步和互斥,使学生学会分析分析进程(线程)竞争资源现象,学习通过信号量解决进程互斥的方法。 实验原理:利用信号量机制解决进程(线程)的基本方法。 实验仪器:计算机一台。 ...
  • 在做多线程开发时,互斥锁是必不可少的。但c语言不像c++11有标准的线程库,在各种编译器支持的平台都...本资源在Windows、Linux、Android、IOS都可以使用。https://blog.csdn.net/u013113678/article/details/120372980
  • 操作系统 互斥到底是什么Operating systems are mainly runs processes and applications to share resources of the system. These resources may CPU, RAM, Network Connection etc. Mutual Exclusion Object a.k....
  • 学习python多任务
  • C++多线程系列(二)线程互斥

    千次阅读 2016-06-30 20:23:05
    首先了解一下线程互斥的概念,线程互斥说白了就是在进程中多个线程的相互制约,如线程A未执行完毕,其他线程就需要等待! 线程之间的制约关系分为间接相互制约和直接相互制约。 所谓间接相互制约:一个系统中的多个...
  • 互斥(抢夺资源,间接制约)是并发执行的多个进程由于竞争同一资源而产生的相互排斥的关系。同步(直接制约)是进程间共同完成一项任务时直接发生的相互作用的关系。同步进程间具有合作关系,在执行时间上必须按...
  • 自旋锁和互斥锁区别

    2020-07-18 05:33:26
    POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API。线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用就是用Pthreads提供的锁机制(lock)来对多个线程之间共...
  • C# 中线程资源访问互斥

    千次阅读 2016-02-29 20:44:03
    使用mutex,进行互斥访问 示例中运行第一个窗口显示running 此时运行第二个窗口显示 空白 处于等待。若在5秒内在第一个窗口中输入,将释放资源,此时第二个窗口将显示running using System; using System....
  • 互斥和同步

    千次阅读 2019-03-10 12:43:22
    互斥:是指散布在不同进程之间的若干程序片段,当某个进程执行其中的一个程序片段时,其他进程就不能运行它们之中的任一程序片段,只能等到该进程运行完之后才可以继续运行。 同步:是指散布在不同进程之间的若干...
  • 摘要:微信搜索【三桥君】 一、题目 系统有某种共享资源m个提供给n个进程互斥使用,假设进程p1需要该资源数目的最大值是m1,p2需要该资源数目的最大值是m2,…,pn需要该资源数目的最大值是mn,那么满足什么条件,...
  • 这里写目录标题概览临界区临界区的引入临界区的概念进程的同步与互斥的概念解决方法经典同步与互斥问题 概览 临界区 临界区的引入 在系统当中,有些资源允许多个进程共享(磁盘),有些资源只允许进程单独使用...
  • 同步和互斥是进程之间的两种常见关系,本文浅析之
  • 互斥锁 (mutex)

    千次阅读 2019-07-03 17:55:36
    定义互斥锁: struct mutex my_mutex; 初始化互斥锁: mutex_init(&my_mutex); 获取互斥锁: void mutex_lock(struct mutex *lock); int mutex_lock_interruptible(struct mutex *lock); int mutex_trylock...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 180,432
精华内容 72,172
关键字:

互斥资源