"""
线程同步
"""
def add1(a):
a += 1 # 该语句对应4行字节码
def dec(a):
a -= 1
import dis
print(dis.dis(add1))
print(dis.dis(dec))
add # 假设每执行一部切换一次。a为全局变量
"""
1. load a #1 a=0
2. load 1 #3 1
3. + #5 1
4. 赋值给a #7 a=1
以上4步执行过程中,任何一步都可能会发生GIL释放,切换给其它线程。
"""
dec
"""
1. load a #2 a=0
2. load 1 #4 1
3. - #6 -1
4. 赋值给a #8 a=-1
以上4步执行过程中,任何一步都可能会发生GIL释放,切换给其它线程。
"""
通过两个线程执行后,a最终呈现的结果,可能是1,也可能是-1,但a并并等于0; 我们希望的结果是,执行完毕后,a的结果为0
变量共享非常经典的问题:结果不一致。特别是电商web开发,减库存的时候,容易出现这个问题。
5 0 LOAD_FAST 0 (a) # 将a load到内存
2 LOAD_CONST 1 (1) # 将1 load到内存
4 INPLACE_ADD # 执行相加
6 STORE_FAST 0 (a) # 将值赋值给a
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
None
8 0 LOAD_FAST 0 (a) # 将a load到内存
2 LOAD_CONST 1 (1) # 将1 load到内存
4 INPLACE_SUBTRACT # 执行相减
6 STORE_FAST 0 (a) # 将值赋值给a
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
None
为了解决上述问题,希望add1执行时,其它代码段希望只有一个在执行。引出了线程同步机制。
"""
线程同步
"""
from threading import Lock # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
import threading
lock = Lock()
total = 0
def add():
global total
global lock
for i in range(100):
lock.acquire() # 获取锁后,别的线程无法获取这把锁,除非它释放锁
total += 1
lock.release() # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行
def dec():
global total
global lock
for i in range(100):
lock.acquire()
total -= 1
lock.release()
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=dec)
thread1.start()
thread2.start()
thread1.join() # Wait until the thread terminates.
thread2.join()
print(total)
"""
1. 锁会影响性能
2. 锁会引起死锁
"""
"""
死锁范例1
"""
from threading import Lock # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
import threading
lock = Lock()
total = 0
def add():
global total
global lock
for i in range(100):
lock.acquire()
lock.acquire() # 已经死锁。
total += 1
lock.release() # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行
def dec():
global total
global lock
for i in range(100):
lock.acquire()
total -= 1
lock.release()
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=dec)
thread1.start()
thread2.start()
thread1.join() # Wait until the thread terminates.
thread2.join()
print(total)
'''
死锁范例2: 资源竞争
A(a,b)
acquire (a)
acquire (b)
B(a,b)
acquire(b)
acquire(a)
对于A/B,只有同时获取a/b才可运行。如果资源a被A获取,同时资源b被B获取,这样会导致A/B都无法运行造成死锁,上述问题解决办法:让B也是先拿a,再拿b
A(a,b)
acquire (a)
acquire (b)
B(a,b)
acquire(a)
acquire(b)
'''
"""
死锁范例3
"""
from threading import Lock # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
import threading
lock = Lock()
total = 0
def add():
global total
global lock
for i in range(100):
lock.acquire() # 由死锁范例1可知,acquire不可连续调用两次
dosomething()
total += 1
lock.release() # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行
def dosomething():
lock.acquire()
# do something
lock.release()
"""
线程同步
"""
from threading import Lock # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
from threading import RLock # 可重入的锁
import threading
"""RLock允许在同一个线程里面,可以连续调用多次acquire,但是一定要注意,acquire的次数与release的次数相等
线程之间还是竞争关系"""
lock = RLock()
total = 0
def add():
global total
global lock
for i in range(100):
lock.acquire()
lock.acquire()
total += 1
lock.release() # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行
lock.release()
def dec():
global total
global lock
for i in range(100):
lock.acquire()
total -= 1
lock.release()
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=dec)
thread1.start()
thread2.start()
thread1.join() # Wait until the thread terminates.
thread2.join()
print(total) # 0