zoukankan      html  css  js  c++  java
  • 互斥锁与死锁现象

    互斥锁

    我们千方百计实现了程序的异步 提高效率,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题。当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。保证数据安全, 而出现了互斥锁 注意 自己加锁容易出现死锁现象

    加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
    虽然可以用文件共享数据实现进程间通信,但问题是:
      1.效率低(共享数据基于文件,而文件是硬盘上的数据)
      2.需要自己加锁处理

    因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。
    队列和管道都是将数据存放于内存中
    队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,
    我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

    互斥锁的原理

    互斥锁的工作原理就是多个人都要去争抢同一个资源:卫生间,一个人抢到卫生间后上一把锁,其他人都要等着,等到这个完成任务后释放锁,其他人才有可能有一个抢到......
    
    所以互斥锁的原理,就是把并发改成串行,降低了效率,但保证了数据安全,不错乱
    加了互斥锁就没有并发效果了 加上锁只有一个可以运行 互斥锁会把并发变成串行 效率变低了
    
    
    利用的模块
    from multiprocessing import Lock
    

    互斥锁与join区别共同点?

    互斥锁与join区别共同点? (面试题)
    共同点: 都是完成了进程之间的串行.
    区别: join是控制的进程串行,互斥锁是随机的抢占资源.保证了公平性
    

    互斥锁的实列

    # 在生活中我们会遇到一些事情,必须在保证公平的情况下又可以保护数据安全
    # 并发变为串行
    # 以下是打印机实列 我们希望谁先抢到,谁先运行 且是依次执行
    from multiprocessing import Process
    from multiprocessing import Lock
    import os
    import time
    import random
    def task(lock):#模拟打印
        lock.acquire()#加锁
        print(f'{os.getpid()}打印开始了')
        time.sleep(random.randint(1,3))
        print(f'{os.getpid()}打印结束了')
        lock.release()#解锁
    if __name__ == '__main__':
        lock=Lock()
        for i in range(4):
            p=Process(target=task,args=((lock,)))
            p.start()
    # 遇到的问题
    # 为什么加锁要同一把锁 不是同一把会怎么样?
    # 锁就是一个凭证,当只有一把锁时 他保证了运行时的唯一行,如果大家都有了也就不可以约束了
    

    死锁现象

    原理

    死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
    
    
    产生死锁的四个必要条件
        (1)互斥使用(资源独占)
        一个资源每次只能给一个进程使用(比如写操作)
        (2)占有且等待
        进程在申请新的资源的同时,保持对原有资源的占有
        (3)不可抢占
        资源申请者不能强行从资源占有者手中夺取资源,资源只能由占有者自愿释放
        (4)循环等待
        P1等待P2占有的资源,P2等待P3的资源,...Pn等待P1的资源,形成一个进程等待回路
    
    

    解决

    死锁是不应该在程序中出现的,在编写程序时应该尽量避免出现死锁。下面有几种常见的方式用来解决死锁问题:
    1.避免多次锁定。尽量避免同一个线程对多个 Lock 进行锁定。例如上面的死锁程序,主线程要对 A、B 两个对象的 Lock 进行锁定,副线程也要对 A、B 两个对象的 Lock 进行锁定,这就埋下了导致死锁的隐患。
    2.具有相同的加锁顺序。如果多个线程需要对多个 Lock 进行锁定,则应该保证它们以相同的顺序请求加锁。比如上面的死锁程序,主线程先对 A 对象的 Lock 加锁,再对 B 对象的 Lock 加锁;而副线程则先对 B 对象的 Lock 加锁,再对 A 对象的 Lock 加锁。这种加锁顺序很容易形成嵌套锁定,进而导致死锁。如果让主线程、副线程按照相同的顺序加锁,就可以避免这个问题。
    3.使用定时锁。程序在调用 acquire() 方法加锁时可指定 timeout 参数,该参数指定超过 timeout 秒后会自动释放对 Lock 的锁定,这样就可以解开死锁了。
    4.死锁检测。死锁检测是一种依靠算法机制来实现的死锁预防机制,它主要是针对那些不可能实现按序加锁,也不能使用定时锁的场景的。
    5.利用可重复锁
    
  • 相关阅读:
    【二分图最大独立集/最小割】P3355 骑士共存问题
    【费用流+正负费用处理】UVA11613 Acme Corporation
    【费用流】P2517 [HAOI2010]订货
    【最小割】P1361 小M的作物
    【最小割】[SHOI2007]善意的投票
    【最小割+割点转换】[USACO5.4]奶牛的电信Telecowmunication
    数据结构学习笔记——ST表
    图论学习笔记——LCA
    基于CNN的手写数字识别程序
    [Atcoder]M-Solutions 题解
  • 原文地址:https://www.cnblogs.com/saoqiang/p/12388470.html
Copyright © 2011-2022 走看看