互斥锁与死锁现象

互斥锁

我们千方百计实现了程序的异步 提高效率,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用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.利用可重复锁
原文地址:https://www.cnblogs.com/saoqiang/p/12388470.html