python--基础知识点--互斥锁、死锁、Lock、RLock

一、互斥锁

  一个进程中的某些资源是可以由多个线程共享的,因为多个线程是并发进行的,且程序执行时会先被转化为原子操作,多个线程可能在同一时间段内都需要使用共享资源,会形成竞态,如果没有预先对应的同步机制,最终导致运行结果错误。

二、示例说明
  1. from threading import Thread
    
    total = 0
    
    
    def add():
    	global total
        for i in range(1000000):
            total += 1
    
    
    def desc():
    global total
        for i in range(1000000):
            total -= 1
    
    
    if __name__ == '__main__':
        thread_add = Thread(target=add)
        thread_desc = Thread(target=desc)
    
        thread_add.start()
        thread_desc.start()
    
        thread_add.join()
        thread_desc.join()
    
        print(total)
    
    
    """
    第一次运行结果:
    -513053
    
    Process finished with exit code 0
    """
    
    """
    第二次运行结果:
    57426
    
    Process finished with exit code 0
    """
    
    """
    对比两次运行结果,两次结果不同。
    
    原因分析:首先 += 和 -=并不是原子操作,其次python在同一时刻只能有一个线程运行,线程的切换规则中有一
    		条,当某个线程执行了一定行数的字节码时解释器就会对线程进行切换。
    
    (1)a += 1 原子操作过程:
    	i.   load a
    	ii.  load 1
    	iii. +
    	iv.  赋值给a
    	
    	a -= 1 原子操作过程:
    	i.   load a
    	ii.  load 1
    	iii. -
    	iv.  赋值给a
    (2)假设刚开始a=0,且假设执行一行字节码就进行线程切换则两个线程的执行过程如下:
    	线程			原子操作				结果
    	+=			i.   load a			0 (a)
    	-=			i.   load a			0 (a)
    	+=			ii.  load 1			1 (1)
    	-=			ii.  load 1			1 (1)
    	+=			iii. +	
    	-=			iii. -			
    	+=			iv.  赋值给a			1 (a)
    	-=			iv.  赋值给a			-1 (a)
    	切片的过程导致a+=1或a-=1的过程丢失,使得最终的结果是a=1或-1,而不是0。
    """
    
  2. 使用锁机制来解决以上问题
    from threading import Thread, Lock
    
    total = 0
    lock_Lock = Lock()
    
    
    def add():
        global total
        global lock_Lock
        for i in range(1000000):
            lock_Lock.acquire()
            total += 1
            lock_Lock.release()
    
    
    def desc():
        global total
        global lock_Lock
        for i in range(1000000):
            lock_Lock.acquire()
            total -= 1
            lock_Lock.release()
    
    
    if __name__ == '__main__':
        thread_add = Thread(target=add)
        thread_desc = Thread(target=desc)
    
        thread_add.start()
        thread_desc.start()
    
        thread_add.join()
        thread_desc.join()
    
        print(total)
    
    
    """
    第一次运行结果:
    0
    
    Process finished with exit code 0
    """
    
    """
    第二次运行结果:
    0
    
    Process finished with exit code 0
    """
    
    """
    两次运行结果相同;使用锁机制的缺点,运行速度降低,原因,加锁与释放锁都需要耗费时间。
    """
    
三、死锁

死锁几种情况示例:

1. 相互引用死锁

两个线程对a、b两个共享变量竞争造成死锁

import threading
import time

a = 0
b = 0
a_lock = threading.Lock()
b_lock = threading.Lock()

def A():
    print("A已启动")
    global a, b
    if a_lock.acquire():
        try:
            print("A_a")
            time.sleep(1)
            if b_lock.acquire():
                try:
                    print("A_b")
                finally:
                    b_lock.release()
        finally:
            a_lock.release()

def B():
    print("B已启动")
    global a, b
    if b_lock.acquire():
        try:
            print("B_b")
            if a_lock.acquire():
                try:
                    print("B_a")
                finally:
                    a_lock.release()
        finally:
            b_lock.release()

threads = [
    threading.Thread(target=A ),
    threading.Thread(target=B )
]

[t.start() for t in threads]
[t.join() for t in threads]

print(a, b)

"""
执行结果:
A已启动
B已启动
A_a
B_b

"""

2. 某个线程在加锁后运行出错,但在错误处理时没有释放锁,导致另一个线程无法进行加锁操作,造成死锁
import threading
import time

a = 0
a_lock = threading.Lock()


def A():
    print("A已启动")
    global a
    if a_lock.acquire():
        try:
            print("A_a")
            a = 2 / 0
            a_lock.release()
        except Exception as e:
            pass
        finally:
            pass

def B():
    print("B已启动")
    global a
    if a_lock.acquire():
        try:
            print("B_a")
            a = 2 / 1
        finally:
            a_lock.release()

threads = [
    threading.Thread(target=A ),
    threading.Thread(target=B )
]

[t.start() for t in threads]
[t.join() for t in threads]

print(a)


"""
执行结果:
A已启动
B已启动
A_a 

"""
3. 递归死锁
from threading import Thread, Lock

total = 0
lock_Lock = Lock()


def Fibonacci(num):
    if num == 2 or num == 1:
        return 1
    else:
        global total,lock_Lock
        lock_Lock.acquire()
        print("lock_Lock已为total上锁")
        total = Fibonacci(num - 1) + Fibonacci(num - 2)
        lock_Lock.release()
        print("lock_Lock已为total释放锁")
        return total


if __name__ == '__main__':
    thread_Fibonacci = Thread(target=Fibonacci, args=(6,))
    thread_Fibonacci.start()
    thread_Fibonacci.join()
    print(total)
   

"""
运行结果:
lock_Lock已为total上锁
"""

"""
程序被卡住,停止往下运行。
原因:(1)上锁过程,在第一层Fibonacci()中,首先lock_Lock为total上锁,之后又进入第二层Fibonacci(),
	 在第二层Fibonacci()中在没有使用lock_Lock为total释放锁的情况下又使用lock_Lock为total上锁;
	 (2)threading.Lock() 是加载线程的锁对象,是一个基本的锁对象,一次只能一个锁定,其余锁请求,需
	 等待锁释放后才能获取。
"""
三、使用RLock解决递归锁
1. Lock锁与RLock锁的区别

  Lock()作为一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取,否则会发生死锁。
  为解决同一线程中不能多次请求同一资源的问题,python提供了“可重入锁”:threading.RLock,RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源 。

2. 使用RLock锁解决递归锁死锁问题,示例如下:
from threading import Thread, RLock

total = 1
lock_RLock = RLock()


def Fibonacci(num):
    if num == 2 or num == 1:
        return 1
    else:
        global total
        global lock_RLock
        lock_RLock.acquire()
        print("lock_Lock为total上锁=====num:%d" % num)
        total = Fibonacci(num - 1) + Fibonacci(num - 2)
        lock_RLock.release()
        print("lock_Lock为total释放锁===num:%d" % num)
        return total


if __name__ == '__main__':
    thread_Fibonacci = Thread(target=Fibonacci, args=(6,))
    thread_Fibonacci.start()
    thread_Fibonacci.join()
    print(total)


"""
运行结果:
lock_Lock为total上锁=====num:6
lock_Lock为total上锁=====num:5
lock_Lock为total上锁=====num:4
lock_Lock为total上锁=====num:3
lock_Lock为total上锁=====num:2
lock_Lock为total释放锁===num:2
lock_Lock为total释放锁===num:3
lock_Lock为total上锁=====num:2
lock_Lock为total释放锁===num:2
lock_Lock为total释放锁===num:4
lock_Lock为total上锁=====num:3
lock_Lock为total上锁=====num:2
lock_Lock为total释放锁===num:2
lock_Lock为total释放锁===num:3
lock_Lock为total释放锁===num:5
lock_Lock为total上锁=====num:4
lock_Lock为total上锁=====num:3
lock_Lock为total上锁=====num:2
lock_Lock为total释放锁===num:2
lock_Lock为total释放锁===num:3
lock_Lock为total上锁=====num:2
lock_Lock为total释放锁===num:2
lock_Lock为total释放锁===num:4
lock_Lock为total释放锁===num:6
8

Process finished with exit code 0
"""

参考视频
参考博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值