目录
1、线程安全介绍
线程安全的问题最主要还是由线程切换导致的。
线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程不安全案例:
创建2个线程:线程1对num进行一千万次+1的操作;线程2对num进行一千万次-1的操作
import threading
num = 0
def add():
global num
for i in range(10_000_000):
num += 1
def sub():
global num
for i in range(10_000_000):
num -= 1
if __name__ == "__main__":
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 : 645898
num result : -331131
num result : 901713
num最后并不是我们所想象的结果0。因此需要通过锁来保障线程切换的时机。
2、threading5种常见锁
threading模块中提供了5种最常见的锁:
-
同步锁:lock(一次只能放行一个)
-
递归锁:rlock(一次只能放行一个)
-
条件锁:condition(一次可以放行任意个)
-
事件锁:event(一次全部放行)
-
信号量锁:semaphore(一次可以放行特定个)
2.1 同步锁(互斥锁)Lock
(1) 基本介绍及相关方法
- 互斥指的是某一资源同一时刻仅能有一个访问者对其进行访问,具有唯一性和排他性,但是互斥无法限制访问者对资源的访问顺序,即访问是无序的
- 同步是指在互斥的基础上(大多数情况),通过其他机制实现访问者对资源的有序访问
- 同步其实已经实现了互斥,是互斥的一种更为复杂的实现,因为它在互斥的基础上实现了有序访问的特点
# 生成互斥锁对象
lock = threading.Lock()
# 获取锁,未获取到程序将会阻塞(当一个线程在执行被上锁的代码块时,将不允许切换到其他线程)
lock.acquire()
# 释放锁
lock.release()
# 判断该锁对象是否处于上锁状态
lock.locked()
(2) 给案例加lock锁
import threading
num = 0
lock = threading.Lock() # 生成互斥锁对象
def add():
global num
lock.acquire() # 上锁
for i in range(10_000_000):
num += 1
lock.release() # 释放锁
def sub():
global num
lock.acquire() # 上锁
for i in range(10_000_000):
num -= 1
lock.release() # 释放锁
if __name__ == "__main__":
subThread01 = threading.Thread(target=add)
subThread02 = threading.Thread(target=sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print("num result : %s" % num)
执行上述代码,每次结果均为0。
上述代码完全变成了串行的状态,对于这种计算密集型I/O业务来说,还不如直接使用串行化单线程执行来得快,所以这个例子仅作为一个示例,不能概述锁真正的用途。
(3)with语句
由于threading.Lock()对象中实现了enter__()与__exit()方法,故可以使用with语句进行上下文管理形式的加锁解锁操作:
import threading
num = 0
lock = threading.Lock()
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__":
subThread01 = threading.Thread(target=add)
subThread02 = threading.Thread(target=sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print("num result : %s" % num