同一变量在多线之间是共享的,任何一个变量都可以被所有线程修改,当多个线程一起修改同一变量时,很可能互相冲突得不到正确的结果,造成线程安全问题。通过示例看一下:
import threading a = 5 def oper(b): global a a = a - b a = a + b def target(b): for i in range(100000): oper(b) if __name__ == '__main__': m = 10 while m > 0: t1 = threading.Thread(target=target, args=(1,)) t2 = threading.Thread(target=target, args=(2,)) t1.start() t2.start() t1.join() t2.join() print(a) m = m - 1
执行结果:
5 5 5 6 6
正常情况下,oper(b) 操作会使 a 的值保持不变,但从多线程的执行结果来看,我们发现出现了错误的结果,并且每次执行的结果可能不同,通常这种问题我们可以使用加锁的方式解决。
threading.Lock
实现原始锁对象的类,一旦一个线程获得一个锁,会阻塞随后尝试获得锁的线程,直到它被释放,通常称其为互斥锁,它是由 _thread 模块直接扩展实现的。它具有如下方法:
acquire(blocking=True, timeout=-1):可以阻塞或非阻塞地获得锁,参数 blocking 用来设置是否阻塞,timeout 用来设置阻塞时间,当 blocking 为 False 时 timeout 将被忽略。
release():释放锁。
locked():判断是否获得了锁,如果获得了锁则返回 True。
threading.RLock
可重入锁(也称递归锁)类,一旦线程获得了重入锁,同一个线程再次获取它将不阻塞,重入锁必须由获取它的线程释放。它具有如下方法:
- acquire(blocking=True, timeout=-1):解释同上。
- release():解释同上。
我们对上述代码进行加锁操作,如下所示:
import threading # 创建锁 lock = threading.Lock() a = 5 def oper(b): # 获取锁 lock.acquire() global a a = a - b a = a + b # 释放锁 lock.release() def target(b): for i in range(100000): oper(b) if __name__ == '__main__': m = 5 while m > 0: t1 = threading.Thread(target=target, args=(1,)) t2 = threading.Thread(target=target, args=(2,)) t1.start() t2.start() t1.join() t2.join() print(a) m = m - 1
执行结果:
5 5 5 5 5
我们可以尝试多次执行,现在每次都可以获得正确的结果了。