锁的作用
锁是Python提供给我们能够自行操控线程切换的一种手段,使用锁可以让线程的切换变的有序。
一旦线程的切换变的有序后,各个线程之间对数据的访问、修改就变的可控,所以若要保证线程安全,就必须使用锁。
threading模块中提供了5种最常见的锁,下面是按照功能进行划分:
同步锁:lock(一次只能放行一个)
递归锁:rlock(一次只能放行一个)
条件锁:condition(一次可以放行任意个)
事件锁:event(一次全部放行)
信号量锁:semaphore(一次可以放行特定个)
1、Lock() 同步锁
基本介绍
Lock锁的称呼有很多,如:
同步锁
互斥锁
它们是什么意思呢?如下所示:
互斥指的是某一资源同一时刻仅能有一个访问者对其进行访问,具有唯一性和排他性,但是互斥无法限制访问者对资源的访问顺序,即访问是无序的
同步是指在互斥的基础上(大多数情况),通过其他机制实现访问者对资源的有序访问
同步其实已经实现了互斥,是互斥的一种更为复杂的实现,因为它在互斥的基础上实现了有序访问的特点
使用方式
同步锁一次只能放行一个线程,一个被加锁的线程在运行时不会将执行权交出去,只有当该线程被解锁时才会将执行权通过系统调度交由其他线程。
如下所示,使用同步锁解决最上面的问题:
[Python] 纯文本查看 复制代码 import threading
num = 0
def add():
lock.acquire()
global num
for i in range(10_000_000):
num += 1
lock.release()
def sub():
lock.acquire()
global num
for i in range(10_000_000):
num -= 1
lock.release()
if __name__ == "__main__":
lock = threading.Lock()
subThread01 = threading.Thread(target=add)
subThread02 = threading.Thread(target=sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print("num result : %s" % num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
这样这个代码就完全变成了串行的状态,对于这种计算密集型I/O业务来说,还不如直接使用串行化单线程执行来得快,所以这个例子仅作为一个示例,不能概述锁真正的用途。
死锁现象
对于同步锁来说,一次acquire()必须对应一次release(),不能出现连续重复使用多次acquire()后再重复使用多次release()的操作,这样会引起死锁造成程序的阻塞,完全不动了,如下所示:
[Python] 纯文本查看 复制代码 import threading
num = 0
def add():
lock.acquire() # 上锁
lock.acquire() # 死锁
# 不执行
global num
for i in range(10_000_000):
num += 1
lock.release()
lock.release()
def sub():
lock.acquire() # 上锁
lock.acquire() # 死锁
# 不执行
global num
for i in range(10_000_000):
num -= 1
lock.release()
lock.release()
if __name__ == "__main__":
lock = threading.Lock()
subThread01 = threading.Thread(target=add)
subThread02 = threading.Thread(target=sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print("num result : %s" % num)
with语句
由于threading.Lock()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:
[Python] 纯文本查看 复制代码 import threading
num = 0
def add():
with lock:
# 自动加锁
global num
for i in range(10_000_000):
num += 1
# 自动解锁
def sub():
with lock:
# 自动加锁
global num
for i in range(10_000_000):
num -= 1
# 自动解锁
if __name__ == "__main__":
lock = threading.Lock()
subThread01 = threading.Thread(target=add)
subThread02 = threading.Thread(target=sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print("num result : %s" % num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
2、RLock() 递归锁
基本介绍
递归锁是同步锁的一个升级版本,在同步锁的基础上可以做到连续重复使用多次acquire()后再重复使用多次release()的操作,但是一定要注意加锁次数和解锁次数必须一致,否则也将引发死锁现象。
使用方式
以下是递归锁的简单使用,下面这段操作如果使用同步锁则会发生死锁现象,但是递归锁不会:
[Python] 纯文本查看 复制代码 import threading
num = 0
def add():
lock.acquire()
lock.acquire()
global num
for i in range(10_000_000):
num += 1
lock.release()
lock.release()
def sub():
lock.acquire()
lock.acquire()
global num
for i in range(10_000_000):
num -= 1
lock.release()
lock.release()
if __name__ == "__main__":
lock = threading.RLock()
subThread01 = threading.Thread(target=add)
subThread02 = threading.Thread(target=sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print("num result : %s" % num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
with语句
由于threading.RLock()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:
[Python] 纯文本查看 复制代码 import threading
num = 0
def add():
with lock:
# 自动加锁
global num
for i in range(10_000_000):
num += 1
# 自动解锁
def sub():
with lock:
# 自动加锁
global num
for i in range(10_000_000):
num -= 1
# 自动解锁
if __name__ == "__main__":
lock = threading.RLock()
subThread01 = threading.Thread(target=add)
subThread02 = threading.Thread(target=sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print("num result : %s" % num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
【免责声明】本文系转载,原文为公众号Python开发者「云崖君」文章,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与联系我们,我们会予以更改或删除相关文章,以保证您的权益!
|