一、线程锁(互斥锁)
在一个程序内,主进程可以启动很多个线程,这些线程都可以访问主进程的内存空间,在Python中虽然有了GIL,同一时间只有一个线程在运行,可是这些线程的调度都归系统,操作系统有自身的调度规则,所以就可能造成,
- 假设两个线程都在访问 global count= 0, 每个进程都会执行 count +=1 。(1)(2)(3)第一个线程申请GIL然后,读取global count到及进程到 cpu ,(4)然后cpu执行到一半,(5)把这个线程停了,将上下文保存到自身寄存器中。注意这时候没返回结果。这时候解释器就会把GIL释放,
- (6)(7)(8)第二个线程申请GIL执行如上步骤,读取count= 0 ,cpu计算结束后将结果count=1 返回,(10)(11)解释器拿到结果进行赋值,此时count=1,这个进程运行结束,(12)(13)然后cpu从寄存器中第一个线程的执行状态,继续执行,注意这个读取的是寄存器中的上下文,就是第一次执行时 count= 0,及第一个线程的上下文。计算结果返回count =1 返回。就造成了结果的错误。
1.1 python中的线程锁(互斥锁mutex)
使用threading模块中的Lock类,得到一个全局锁实例。然后 Lock()实例下 有一个 acquire 方法为加锁, release 方法释放锁
import threading
count = 0
lock = threading.Lock() #申请一个锁
def count_():
global count #声明为全局变量
lock.acquire() #加锁,锁释放前不予许其他程序执行
count += 1
lock.release() #执行完 ,释放锁
thread_list = []
for i in range(10):
t = threading.Thread(target= count_)
t.start()
thread_list.append(t)
for t in thread_list:
t.join() #主程序等待所有程序执行完毕
print('Count:{: ^4}'.format(count))
1.2 递归锁
基本用不上,主要目的就是防止锁里面加锁,然后程序无法解锁退出。
import threading,time
def run1():
print("grab the first part data")
lock.acquire()
global num
num +=1
lock.release()
return num
def run2():
print("grab the second part data")
lock.acquire()
global num2
num2+=1
lock.release()
return num2
def run3():
lock.acquire()
res = run1()
print('--------between run1 and run2-----')
res2 = run2()
lock.release()
print(res,res2)
if __name__ == '__main__':
num,num2 = 0,0
lock = threading.RLock()
for i in range(10):
t = threading.Thread(target=run3)
t.start()
while threading.active_count() != 1:
print(threading.active_count())
else:
print('----all threads done---')
print(num,num2)