在多线程同时请求同一个资源并进行修改时可能会造成结果出错,例如共享变量
from dis import dis
import threading
a = 0
def add():
global a
a += 1
def desc():
global a
a -= 1
print(dis(add))
print(dis(desc))
thread_add = threading.Thread(target=add)
thread_desc = threading.Thread(target=desc)
thread_add.start()
thread_desc.start()
thread_add.join()
thread_desc.join()
print(a)
在上面的代码中存在一个什么问题呢?虽然运行没能看出什么问题,看dis这个模块打印出的内容,一个+=运算是先load a 然后load 1 然后做运算再把结果赋值给a。那问题来了如果a同时被desc和add 加载呢,那desc和add加载进去的a值都为0,那a做add和desc运算后结果可能为1 或者-1而不是0。
0 LOAD_GLOBAL 0 (a)
2 LOAD_CONST 1 (1)
4 INPLACE_ADD
6 STORE_GLOBAL 0 (a)
8 LOAD_CONST 0 (None)
要怎么解决这个同步问题呢,解决方法是给资源加把锁Lock,RLock(可重如的锁)
from threading import Lock
total = 0
lock = Lock()
def add():
global total
global lock
#1.do something1
#2. io操作
#3.do someting3
for i in range(1000000):
lock.acquire()
total += 1
lock.release()
def desc():
global total
global lock
for i in range(1000000):
lock.acquire()
total -= 1
lock.release()
import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
print(dis(add))
Lock在使用过程中不可以重入,重入会造成死锁,比如
lock.acquire()
lock.acquire()
而CLock是在同一线程中是可以重入的,但要记得释放相同数量的锁。