精华内容
下载资源
问答
  • 2021-01-05 20:13:56

    互斥的作用:保护数据的完整性
    进程间通信会引起死锁,(两个都等互相发消息过来),可能会有饥饿现象

    互斥的要求:
    一次只允许一个资源去使用临界起源
    在非临界区终止了这个进程的执行,也不能影响其他进程
    不允许在互斥的时候产生死锁与饥饿
    如果没有进程在访问临界区,那么就立马把申请的进程拿进去使用

    互斥条件:空闲让进
    忙则等待
    有限等待
    让权等待

    互斥的解决方案:软件、硬件、信号量、Monitors(管权)、信息传递;
    硬件方法:
    1.屏蔽中断:独占资源,代价很高,遇到紧急情况无法去解决,多处理器其他处理器也会来竞争
    2.专用机器指令:一个指令和另一个指令之间避免了冲突(test set、exchange)两个都能够控制任意多个进程
    缺点:出现忙等,消耗了处理机的时间
    可能出现饥饿现象
    可能会产生死锁

    Semaphores(信号量):一种用来进程之间交换信息的特殊变量
    Wait(s)、signal(s)是对信号量操作的原语,是一段代码,在执行操作时不能被中断;
    信号量包含一个整形量,可以被初始化为一个非负整数;
    Wait操作对信号量做递减操作:wait(s):S-1 wait操作:申请资源且可能阻塞自己(s<0)
    signal操作对信号量做加法操作:signal(s):S+1 signal操作:释放资源并唤醒阻塞进程(s<=0)
    General Semaphore(通用信号量):是记录型,其中一个域为整型,另一个域为队列,其元素为等待该信号量的阻塞进程(FIFO)
    Binary Semaphore(二进制信号量):只能是0、1

    信号量的类型:互斥信号量、资源信号量
    互斥信号量用于申请或释放资源的使用权,常初始化为1
    资源信号量用于申请或归还资源,可以初始化为大于1的正整数,表示系统中某资源的可用个数
    Wait操作用于申请资源(或使用权),进行wait原语时,可能会阻塞自己;
    Signal操作用于释放资源(或归还资源使用权),进程执行signal原语时,有责任唤醒一个阻塞进程

    信号量的意义:
    1.互斥信号量:申请/释放使用权,常初始化为1
    2.资源信号量:申请/归还资源,资源信号量可以初始化为一个正整数

    操作系统内核通过系统调用形式提供wait和signal原语,应用程序通过系统调用实现进程间的互斥
    工程实践证明,利用信号量方法实现进程互斥是高效的,一直被广泛采用。

    生产者消费者问题:
    生产者:产生数据并将数据放入缓冲区(buffer)
    消费者:一个消费者一次取一条数据
    注意:1.进程先申请资源信号量,再申请互斥信号量,顺序不能颠倒。
    2.对任何信号量的wait与signal操作必须配对。同一进程中的多对wait与signal语句只能嵌套,不能交叉。
    3.对同一个信号量的wait与signal可以不在同一个进程中。
    4.wait与signal语句不能颠倒顺序,wait语句一定先于signal语句。

    读者写者问题:
    可用于解决多个进程共享一个数据区(文件、内存区、一组寄存器等),其中若干读进程只能读数据,若干写进程只能写数据等实际问题。
    读者优先:一旦有读者正在读数据,允许多个读者同时进入读数据,只有当全部读者退出,才允许写者进入写数据。(写者容易饥饿)

    更多相关内容
  • 终端登录互斥方案的实现

    千次阅读 2017-11-15 18:11:00
    在一些业务中要求不允许终端同时使用同一帐号登录。只有在账户不活跃的情况下,才允许其他设备登录。 实现方案如下。 本文转自 hgditren 51CTO博客,原文链接:http://blog.51cto.com/phpme/1924138,...

            在一些业务中要求不允许多终端同时使用同一帐号登录。只有在账户不活跃的情况下,才允许其他设备登录。

    实现方案如下。


    wKioL1kSv72RVrH-AACcnmZEhgo998.png










    本文转自 hgditren 51CTO博客,原文链接:http://blog.51cto.com/phpme/1924138,如需转载请自行联系原作者
    展开全文
  • 实现互斥的几种方案

    2021-08-13 19:13:53
    实现互斥方案忙等待的互斥屏蔽中断锁变量严格轮换法Peterson解法TSL指令睡眠与唤醒生产者-消费者问题 忙等待的互斥 屏蔽中断 在单处理器系统中,最简单的方法是使每进程在刚刚进入临界区后立即屏蔽所有中断,并...

    忙等待的互斥

    屏蔽中断

    在单处理器系统中,最简单的方法是使每个进程在刚刚进入临界区后立即屏蔽所有中断,并在就要离开之前再打开中断。屏蔽中断后,时钟中断也被屏蔽。CPU只有发生时钟中断或其他中断时才会进行进程切换,这样,在屏蔽中断之后CPU将不会被切换到其他进程。于是,一旦某个进程屏蔽中断之后,它就可以检查和修改共享内存,而不必担心其他进程介入

    这个方案并不好,因为把屏蔽中断的权力交给用户进程是不明智的。设想一下,若一个进程屏蔽中断后 不再打开中断,其结果将会如何?整个系统可能会因此终止。而且,如果系统是多处理器(有两个或可能更多的处理器),则屏蔽中断仅仅对执行disable指令的那个CPU有效。其他CPU仍将继续运行,并可以访问共享内存

    另一方面,对内核来说,当它在更新变量或列表的几条指令期间将中断屏蔽是很方便的。当就绪进程队 列之类的数据状态不一致时发生中断,则将导致竞争条件。所以结论是:屏蔽中断对于操作系统本身而言是 一项很有用的技术,但对于用户进程则不是一种合适的通用互斥机制

    由于多核芯片的数量越来越多,即使在低端PC上也是如此。因此,通过屏蔽中断来达到互斥的可能性 ——甚至在内核中——变得日益减少了。在一个多核系统中(例如,多处理器系统),屏蔽一个CPU的中断不会阻止其 他CPU干预第一个CPU所做的操作。结果是人们需要更加复杂的计划

    锁变量

    作为第二种尝试,可以寻找一种软件解决方案。设想有一个共享(锁)变量,其初始值为0。当一个进程想进入其临界区时,它首先测试这把锁。如果该锁的值为0,则该进程将其设置为1并进入临界区。若这把锁的值已经为1,则该进程将等待直到其值变为0。于是,0就表示临界区内没有进程,1表示已经有某个进程进入临界区

    但是,这种想法也包含了与假脱机目录一样的疏漏。假设一个进程读出锁变量的值并发现它为0,而恰好在它将其值设置为1之前,另一个进程被调度运行,将该锁变量设置为1。当第一个进程再次能运行时,它同样也将该锁设置为1,则此时同时有两个进程进入临界区中

    可能会想,先读出锁变量,紧接着在改变其值之前再检查一遍它的值,这样便可以解决问题。但这实际上无济于事,如果第二个进程恰好在第一个进程完成第二次检查之后修改了锁变量的值,则同样还会发生竞争条件

    严格轮换法

    第三种互斥的方法如图所示
    在这里插入图片描述
    这里的程序段用C语言编写。之所以选择C语言是由于实际的操作系统普遍用C语言编写(或偶尔用C++),而基本上不用像Java、Modula3或 Pascal这样的语言。对于编写操作系统而言,C语言是强大、有效、可预知和有特性的语言。而对于Java,它就不是可预知的,因为它在关键时刻会用完存储器,而在不合适的时候会调用垃圾收集程序回收内存。在C语言中,这种情形就不可能发生,因为C语言中不需要进行空间回收

    在图中,整型变量turn,初始值为0,用于记录轮到哪个进程进入临界区,并检查或更新共享内存。 开始时,进程0检查turn,发现其值为0,于是进入临界区。进程1也发现其值为0,所以在一个等待循环中不 停地测试turn,看其值何时变为1。连续测试一个变量直到某个值出现为止,称为忙等待(busy waiting)。 由于这种方式浪费CPU时间,所以通常应该避免

    只有在有理由认为等待时间是非常短的情形下,才使用忙等待。用于忙等待的锁,称为自旋锁(spin lock)

    进程0离开临界区时,它将turn的值设置为1,以便允许进程1进入其临界区。假设进程1很快便离开了临 界区,则此时两个进程都处于临界区之外,turn的值又被设置为0。现在进程0很快就执行完其整个循环,它退出临界区,并将turn的值设置为1。此时,turn的值为1,两个进程都在其临界区外执行

    突然,进程0结束了非临界区的操作并且返回到循环的开始。但是,这时它不能进入临界区,因为turn的当前值为1,而此时进程1还在忙于非临界区的操作,进程0只有继续while循环,直到进程1把turn的值改为0。这说明,在一个进程比另一个慢了很多的情况下,轮流进入临界区并不是一个好办法

    这种情况违反了前面叙述的条件3:进程0被一个临界区之外的进程阻塞。再回到前面假脱机目录的问题,如果我们现在将临界区与读写假脱机目录相联系,则进程0有可能因为进程1在做其他事情而被禁止打印另一个文件

    Peterson解法

    Peterson的算法如图所示。该算法由两个用ANSI C编写的过程组成。ANSI C要求为所定义和使用的所有函数提供函数原型

    在这里插入图片描述
    在使用共享变量(即进入其临界区)之前,各个进程使用其进程号0或1作为参数来调用enter_region。 该调用在需要时将使进程等待,直到能安全地进入临界区。在完成对共享变量的操作之后,进程将调用 leave_region,表示操作已完成,若其他的进程希望进入临界区,则现在就可以进入

    一开始,没有任何进程处于临界区中,现在进程0调用 enter_region。它通过设置其数组元素和将turn置为0来标识它希望进入临界区。由于进程1并不想进入临界区,所以enter_region很快便返回。如果进程1现在调用enter_region,进程1将在此处挂起直到interested[0]变成FALSE,该事件只有在进程0调用leave_region退出临界区时才会发生

    现在考虑两个进程几乎同时调用enter_region的情况。它们都将自己的进程号存入turn,但只有后被保存进去的进程号才有效,前一个因被重写而丢失。假设进程1是后存入的,则turn为1。当两个进程都运行到while语句时,进程0将循环0次并进入临界区,而进程1则将不停地循环且不能进入临界区,直到进程0退出临界区为止

    TSL指令

    现在来看需要硬件支持的一种方案。某些计算机中,特别是那些设计为多处理器的计算机,都有下面一 条指令:

    TSL RX,LOCK
    

    称为测试并加锁(Test and Set Lock),它将一个内存字lock读到寄存器RX中,然后在该内存地址上存一个非零值。读字和写字操作保证是不可分割的,即该指令结束之前其他处理器均不允许访问该内存字。执行TSL指令的CPU将锁住内存总线,以禁止其他CPU在本指令结束之前访问内存

    锁住存储总线不同于屏蔽中断。屏蔽中断,然后在读内存字之后跟着写操作并不能阻止 总线上的第二个处理器在读操作和写操作之间访问该内存字。事实上,在处理器1上屏蔽中断对处理器2根本没有任何影响。让处理器2远离内存直到处理器1完成的惟一方法就是锁住总线,这需要一个特殊的硬件设施 (基本上,一根总线就可以确保总线由锁住它的处理器使用,而其他的处理器不能用)

    为了使用TSL指令,要使用一个共享变量lock来协调对共享内存的访问。当lock为0时,任何进程都可以使用TSL指令将其设置为1,并读写共享内存。当操作结束时,进程用一条普通的move指令将lock的值重新设置为0

    这条指令如何防止两个进程同时进入临界区呢?解决方案如图所示
    在这里插入图片描述
    第一条指令将lock原来的值复制到寄存器中并将lock设置为1,随后这个原来的值与0相比较。如果它非零,则说明以前已被加锁,则程序将回到开始并再次测试。经过或长或短的一段时间后,该值将变为0(当前处于临界区中的进程退出临界区时),于是过程返回,此时已加锁。要清除这个锁非常简单,程序只需将0存入lock即可,不需要特殊的同步指令

    现在有一种很明确的解法了。进程在进入临界区之前先调用enter_region,这将导致忙等待,直到锁空闲为止,随后它获得该锁并返回。在进程从临界区返回时它调用leave_region,这将把lock设置为0。与基于临界区问题的所有解法一样,进程必须在正确的时间调用enter_region和leave_region,解法才能奏效。如果一个进程有欺诈行为,则互斥将会失败

    睡眠与唤醒

    Peterson解法和TSL或XCHG解法都是正确的,但它们都有忙等待的缺点。这些解法在本质上是这样的: 当一个进程想进入临界区时,先检查是否允许进入,若不允许,则该进程将原地等待,直到允许为止

    这种方法不仅浪费了CPU时间,而且还可能引起预想不到的结果。考虑一台计算机有两个进程,H优先级较高,L优先级较低。调度规则规定,只要H处于就绪态它就可以运行。在某一时刻,L处于临界区中,此时H变到就绪态,准备运行(例如,一条I/O操作结束)。现在H开始忙等待,但由于当H就绪时L不会被调 度,也就无法离开临界区,所以H将永远忙等待下去。这种情况有时被称作优先级反转问题

    现在来考察几条进程间通信原语,它们在无法进入临界区时将阻塞,而不是忙等待。最简单的是sleep和wakeup。sleep是一个将引起调用进程阻塞的系统调用,即被挂起,直到另外一个进程将其唤醒。wakeup 调用有一个参数,即要被唤醒的进程。另一种方法是让sleep和wakeup各有一个参数,即有一个用于匹配sleep和wakeup的内存地址

    生产者-消费者问题

    作为使用这些原语的一个例子,我们考虑生产者-消费者(producer-consumer)问题,也称作有界缓冲 区(bounded-buffer)问题。两个进程共享一个公共的固定大小的缓冲区。其中一个是生产者,将信息放入缓冲区;另一个是消费者,从缓冲区中取出信息

    问题在于当缓冲区已满,而此时生产者还想向其中放入一个新的数据项的情况。其解决办法是让生产者睡眠,待消费者从缓冲区中取出一个或多个数据项时再唤醒它。同样地,当消费者试图从缓冲区中取数据而发现缓冲区为空时,消费者就睡眠,直到生产者向其中放入一些数据时再将其唤醒

    这个方法听起来很简单,但它包含与前边假脱机目录问题一样的竞争条件。为了跟踪缓冲区中的数据项数,我们需要一个变量count。如果缓冲区最多存放N个数据项,则生产者代码将首先检查count是否达到N, 若是,则生产者睡眠;否则生产者向缓冲区中放入一个数据项并增量count的值

    消费者的代码与此类似:首先测试count是否为0,若是,则睡眠;否则从中取走一个数据项并递减count的值。每个进程同时也检测另一个进程是否应被唤醒,若是则唤醒之。生产者和消费者的代码如图所示
    在这里插入图片描述
    为了在C语言中表示sleep和wakeup这样的系统调用,我们将以库函数调用的形式来表示。尽管它们不是标准C库的一部分,但在实际上任何系统中都具有这些库函数。未列出的过程insert_item和remove_item用来记录将数据项放入缓冲区和从缓冲区取出数据等事项

    现在回到竞争条件的问题。这里有可能会出现竞争条件,其原因是对count的访问未加限制。有可能出现以下情况:缓冲区为空,消费者刚刚读取count的值发现它为0。此时调度程序决定暂停消费者并启动运行生产者。生产者向缓冲区中加入一个数据项,count加1。现在count的值变成了1。它推断认为由于count刚才为0,所以消费者此时一定在睡眠,于是生产者调用wakeup来唤醒消费者

    但是,消费者此时在逻辑上并未睡眠,所以wakeup信号丢失。当消费者下次运行时,它将测试先前读 到的count值,发现它为0,于是睡眠。生产者迟早会填满整个缓冲区,然后睡眠。这样一来,两个进程都将永远睡眠下去

    问题的实质在于发给一个(尚)未睡眠进程的wakeup信号丢失了。如果它没有丢失,则一切都很正常。一种快速的弥补方法是修改规则,加上一个唤醒等待位。当一个wakeup信号发送给一个清醒的进程信号时,将该位置1。随后,当该进程要睡眠时,如果唤醒等待位为1,则将该位清除,而该进程仍然保持清 醒。唤醒等待位实际上就是wakeup信号的一个小仓库

    尽管在这个简单例子中用唤醒等待位的方法解决了问题,但是我们很容易就可以构造出一些例子,其中 有三个或更多的进程,这时一个唤醒等待位就不够使用了。于是我们可以再打一个补丁,加入第二个唤醒等 待位,甚至是8个、32个等,但原则上讲,这并没有从根本上解决问题

    展开全文
  • 一把互斥锁保护多个资源

    万次阅读 2021-12-31 23:23:19
    一把互斥锁保护多个资源前言保护没有关联关系的多个资源保护有关联关系的多个资源使用锁的正确姿势总结 前言 在上一篇文章中,我们提到受保护资源和锁之间合理的关联关系应该是 N:1 的关系,也就是说可以用一把锁...


    前言

    受保护资源和锁之间合理的关联关系应该是 N:1 的关系,可以用一把锁来保护多个资源,但是不能用多把锁来保护一个资源

    当要保护多个资源时,首先要区分这些资源是否存在关联关系。

    保护没有关联关系的多个资源

    在现实世界里,球场的座位和电影院的座位就是没有关联关系的,球赛有球赛的门票,电影院有电影院的门票,各自管理各自的。

    对应到编程领域,例如,银行业务中有针对账户余额(余额是一种资源)的取款操作,也有针对账户密码(密码也是一种资源)的更改操作,可以为账户余额和账户密码分配不同的锁来解决并发问题。

    相关的示例代码如下,账户类 Account 有两个成员变量,分别是账户余额 balance 和账户密码 password。取款 withdraw() 和查看余额 getBalance() 操作会访问账户余额 balance,创建一个 final 对象 balLock 作为锁(类比球赛门票);而更改密码 updatePassword() 和查看密码 getPassword() 操作会修改账户密码 password,创建一个 final 对象 pwLock 作为锁(类比电影票)。不同的资源用不同的锁保护,各自管各自的。

    class Account {
      // 锁:保护账户余额
      private final Object balLock = new Object();
      // 账户余额  
      private Integer balance;
      // 锁:保护账户密码
      private final Object pwLock = new Object();
      // 账户密码
      private String password;
     
      // 取款
      void withdraw(Integer amt) {
        synchronized(balLock) {
          if (this.balance > amt){
            this.balance -= amt;
          }
        }
      } 
      // 查看余额
      Integer getBalance() {
        synchronized(balLock) {
          return balance;
        }
      }
     
      // 更改密码
      void updatePassword(String pw){
        synchronized(pwLock) {
          this.password = pw;
        }
      } 
      // 查看密码
      String getPassword() {
        synchronized(pwLock) {
          return password;
        }
      }
    }
    
    

    当然也可以用一把互斥锁来保护多个资源,例如可以用 this 这一把锁来管理账户类里所有的资源:账户余额和用户密码。具体实现很简单,示例程序中所有的方法都增加同步关键字 synchronized 就可以了。

    但是用一把锁有个问题,就是性能太差,会导致取款、查看余额、修改密码、查看密码这四个操作都是串行的。而用两把锁,取款和修改密码是可以并行的。用不同的锁对受保护资源进行精细化管理,能够提升性能。这种锁还有个名字,叫细粒度锁

    保护有关联关系的多个资源

    如果多个资源是有关联关系的。例如银行业务里面的转账操作,账户 A 减少 100 元,账户 B 增加 100 元。这两个账户就是有关联关系的。

    声明账户类:Account,该类有一个成员变量余额:balance,还有一个用于转账的方法:transfer(),然后怎么保证转账操作 transfer() 没有并发问题呢?

    class Account {
      private int balance;
      // 转账
      void transfer( Account target, int amt){
        	if (this.balance > amt) {
         	 this.balance -= amt;
          	target.balance += amt;
         }
      } 
    }
    

    直觉方案:用户 synchronized 关键字修饰一下 transfer() 方法就可以了。

    class Account {
      private int balance;
      // 转账
      synchronized void transfer(
          Account target, int amt){
        if (this.balance > amt) {
          this.balance -= amt;
          target.balance += amt;
        }
      } 
    }
    

    在这段代码中,临界区内有两个资源,分别是转出账户的余额 this.balance 和转入账户的余额 target.balance,并且用的是一把锁 this,符合多个资源可以用一把锁来保护,可惜,这个方案仅仅是看似正确。

    问题就出在:this 这把锁可以保护自己的余额 this.balance,却保护不了别人的余额 target.balance,就像你不能用自家的锁来保护别人家的资产,也不能用自己的票来保护别人的座位一样。

    在这里插入图片描述

    具体分析一下,假设有 A、B、C 三个账户,余额都是 200 元,用两个线程分别执行两个转账操作:账户 A 转给账户 B 100 元,账户 B 转给账户 C 100 元,期望的结果是账户 A 的余额是 100 元,账户 B 的余额是 200 元, 账户 C 的余额是 300 元。

    假设线程 1 执行账户 A 转账户 B 的操作,线程 2 执行账户 B 转账户 C 的操作。这两个线程分别在两颗 CPU 上同时执行,期望它们是互斥的,但实际上并不是。因为线程 1 锁定的是账户 A 的实例(A.this),而线程 2 锁定的是账户 B 的实例(B.this),所以这两个线程可以同时进入临界区 transfer()。同时进入临界区的结果是:线程 1 和线程 2 都会读到账户 B 的余额为 200,导致最终账户 B 的余额可能是 300(线程 1 后于线程 2 写 B.balance,线程 2 写的 B.balance 值被线程 1 覆盖),可能是 100(线程 1 先于线程 2 写 B.balance,线程 1 写的 B.balance 值被线程 2 覆盖),就是不可能是 200。

    在这里插入图片描述

    使用锁的正确姿势

    用同一把锁来保护多个资源,也就是现实世界的“包场”,在编程领域“包场”只要锁能覆盖所有受保护资源就可以了

    在上面的例子中,this 是对象级别的锁,所以 A 对象和 B 对象都有自己的锁。如果让 A 对象和 B 对象共享一把锁,可以让所有对象都持有一个唯一性的对象,这个对象在创建 Account 时传入。示例代码如下,把 Account 默认构造函数变为 private,同时增加一个带 Object lock 参数的构造函数,创建 Account 对象时,传入相同的 lock,这样所有的 Account 对象都会共享这个 lock 了。

    class Account {
      private Object lock;
      private int balance;
      private Account();
      // 创建 Account 时传入同一个 lock 对象
      public Account(Object lock) {
        this.lock = lock;
      } 
      // 转账
      void transfer(Account target, int amt){
        // 此处检查所有对象共享的锁
        synchronized(lock) {
          if (this.balance > amt) {
            this.balance -= amt;
            target.balance += amt;
          }
        }
      }
    }
    

    这个办法确实能解决问题,但是有点小瑕疵,它要求在创建 Account 对象的时候必须传入同一个对象,如果创建 Account 对象时,传入的 lock 不是同一个对象,会出现锁自家门来保护他家资产的荒唐事。在真实的项目场景中,创建 Account 对象的代码很可能分散在多个工程中,传入共享的 lock 真的很难。

    所以,上面的方案缺乏实践的可行性,需要更好的方案。就是用 Account.class 作为共享的锁。Account.class 是所有 Account 对象共享的,而且这个对象是 Java 虚拟机在加载 Account 类的时候创建的,所以不用担心它的唯一性。使用 Account.class 作为共享的锁,就无需在创建 Account 对象时传入了,代码更简单。

    class Account {
      private int balance;
      // 转账
      void transfer(Account target, int amt){
        synchronized(Account.class) {
          if (this.balance > amt) {
            this.balance -= amt;
            target.balance += amt;
          }
        }
      } 
    }
    

    下面这幅图很直观地展示了如何使用共享的锁 Account.class 来保护不同对象的临界区的。

    在这里插入图片描述

    总结

    一把锁如何保护多个资源的关键是要分析多个资源之间的关系。

    • 如果资源之间没有关系,很好处理,每个资源一把锁就可以了。
    • 如果资源之间有关联关系,就要选择一个粒度更大的锁,这个锁应该能够覆盖所有相关的资源。除此之外,还要梳理出有哪些访问路径,所有的访问路径都要设置合适的锁,这个过程可以类比一下门票管理。

    关联关系如果用更具体、更专业的语言来描述的话,其实是一种“原子性”特征:主要是面向 CPU 指令的,转账操作的原子性则是属于是面向高级语言的,不过它们本质上是一样的。

    “原子性”的本质是什么?其实不是不可分割,不可分割只是外在表现,其本质是多个资源间有一致性的要求,操作的中间状态对外不可见。例如,在 32 位的机器上写 long 型变量有中间状态(只写了 64 位中的 32 位),在银行转账的操作中也有中间状态(账户 A 减少了 100,账户 B 还没来得及发生变化)。所以解决原子性问题,是要保证中间状态对外不可见。

    展开全文
  • 文章目录一、线程安全与互斥(案例说明)1....2、设置三窗口同时卖电影票 3、观察卖电影票的过程 2.代码实现 代码如下(SellTicket线程): public class SellTicket implements Runnable { // 定义100张票 p
  • 本文章节安排如下:案例思路概述设计约束模型创建系统架构模型创建计算分析模型-参数图变体的实现-Generalizationrelationship多方案对比分析小结&项目应用展望如上图所示,系统多方案设计和比选大致分为四步骤:
  • 多线程的优势在于并发性,即可以同时运行多个任务。但是当线程需要使用共享数据时,也可能会由于数据不同步产生“错误情况”,这是由系统的线程调度具有一定的随机性造成的。 由于线程之间的任务执行是CPU进行随机...
  • 也就是某一时刻不允许多个进程同时访问,只能单个进程的访问。我们把这些程序的片段称作临界区或临界段,它存在的目的是有效的防止竞争条件又能保证最大化使用共享数据。而这些并发进程必须有好的解决方案,...
  • 实际应用中, 可能会是去 open 一 I2C 设备节点, 这 I2C 控制着一 I2C 路分配器(例如1分8) 8路 I2C 连接着8相同的 ...为了进程共同访问这 I2C 资源, 就要用信号量做同步和互斥了 // semaphore.c #in...
  • html代码: &amp;lt;textarea id=&quot;editor1&quot; style=&quot;height: 500px;&quot; name=&quot;editor1&quot; type=&quot;text/html&quot;...editor
  • 线程的同步与互斥

    2018-03-17 17:53:28
    多个控制线程共享相同的内存时,要确保对数据访问的正确性,就需要做线程的同步与互斥工作。先看下面这个例子: 为什么要进行线程的同步与互斥 #include &lt;stdio.h&gt; #include &lt;stdlib...
  • C++线程并发(二)---线程同步之互斥

    万次阅读 多人点赞 2019-03-20 00:08:29
    一、何为线程同步 在前一篇文章《C++多线程并发编程...线程间通信:一个任务被分割为多个线程并发处理,多个线程可能都要处理某一共享内存的数据,多个线程对同一共享内存数据的访问需要准确有序。 如果像前一篇文...
  • Java 互斥锁:如何用一把锁保护多个资源 怎么用一把锁保护多个资源? 当我们要保护多个资源时,首先要区分这些资源是否存在关联关系。 保护没有关联关系的多个资源 不同的资源用不同的锁保护,各自管各自的。 class...
  • 解决互斥的五种方法

    千次阅读 2019-05-30 19:58:01
    互斥量:为协调一起对一共享资源的单独访问而设计的。 信号量:为控制一具备有限数量用户资源而设计。 事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 队列:排队处理。 临界区(Critical Section...
  • 解决线程编程中的同步互斥问题

    千次阅读 2016-12-03 11:54:39
    一、解决线程编程中的同步互斥问题, 1、可以使用关键段CRITICAL_SECTIONI来解决。 2、关键段CRITICAL_SECTION一共有四函数,分为初始化,销毁,进入关键区域、离开关键区域。(关键段一般用CS代替) (1)、...
  • 很简单,简单点写就是存一key到redis,设置失效时间,当这key存在就是锁着 稍微复杂点就是实现jdk的Lock接口,在lock()方法或者tryLock()方法中写添加redis的逻辑,在unlock()写删除redis key的逻辑 ...
  • 在win平台下,如果要进程共享访问同一把互斥,则使用有名CreateMutex或者有名CreateFileMapping。 例如: HANDLE mutex = CreateMutexA(NULL, false, "MyMutexName"); 使用上面的代码在同一用户、同...
  • 80-线程互斥与同步

    千次阅读 2017-03-10 09:56:52
    线程互斥与同步;竞态错误的原因;互斥与同步的区别。
  • “小顺子啊,你看咱们的APP登录能不能加一功能,就是那种…那个…一用户登录之后,能把上一登录的自动挤下线” 此时的你陷入了沉思,怎么让他在登录之后,把上一登录者的会话给挤下线呢? 难道说要在每次...
  • Java中的线程安全(2)之互斥同步的底层原理 本来在理解完互斥同步之后,应该开始扒一扒Lock的源码了,但是在真正进入源码阶段之前,需要先按照...如果多个线程都想要访问这段代码,需要一定的机制,让非正在访问的线程
  • 互斥和同步

    千次阅读 2016-08-10 17:10:35
    确保两或更的进程在关键活动中不会出现交叉,比如两进程试图争夺同一资源 正确的顺序,某个进程运行前必须等待另外一进程的运行完、 注意后面两问题和解决方法同样适用于线程1、互斥原子性(Atomic):连续...
  • 互斥一组并发进程中的一个或多个程序段因共享某一公有资源而导致他们不能同时进入临界区称为互斥;互斥方案应满足的4个条件;互斥的实现方案;关中断;锁变量;严格轮转法;Peterson方案;TSL指令测试与设置指令;信号量...
  • 深入理解互斥

    2021-08-16 14:31:32
    如果不需要信号量的计数能力,有时可以使用信号量的一简化版本,称为互斥量(mutex)。互斥量仅仅适用于管理共享资源或一小段代码。由于互斥量在实现时既容易又有效,这使得互斥量在实现用户空间线程包时非常有用 ...
  • 话不说直接入正题,首先了解Redis 缓存穿透,击穿是什么概念; 缓存穿透:指单个热点key大量请求,而redis缓存由于某种原因刚好失效,直接请求DB,导致数据库承受不住压力宕机,服务因此被干懵了; 一般这种问题...
  • Android 实现两组RadioGroup互斥

    千次阅读 2021-11-25 08:36:41
    在app开发中,会遇到两组RadioGroup来做选择项,只能选中其中的某一选项,这就需要两组RadioGroup实现互斥 而RadioGroup 有clearCheck()实现清除选中的方法 和 check的方法来默认选中哪个项 具体解决方案如下: 当...
  • 场景:多个地方同时向一个文件写数据,如何保持写数据的互斥性。
  • 信号的同步互斥——五经典问题

    千次阅读 2020-04-28 11:56:55
    有两进程:一组生产者进程和一组消费者进程共享一初始为空、固定大小为n的缓存(缓冲区)。生产者的工作是制造一段数据,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待,如此反复; 同时,只有...
  • Linux互斥锁及其应用

    千次阅读 2020-07-27 21:34:02
    文章目录互斥锁1.1锁的创建1.2 锁操作1.3 锁销毁1.4互斥锁属性初始化互斥锁属性对象pthread_mutexattr_init 语法pthread_mutexattr_init 返回值销毁...在线程实际运行过程中,我们经常需要多个线程保持同步。这时可以
  • 在上一篇文章中,我们提到受保护资源和锁之间合理的关联关系应该是 N:1 的关系,也就是说可以用一把锁来保护多个资源,但是不能用多把锁来保护一个资源,并且结合文中示例,我们也重点强调了“不能用多把锁来保护一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 68,157
精华内容 27,262
关键字:

多个互斥方案比较