线程的数据安全
1 数据混乱现象
from threading import Thread,Lock num = 0 lst = [] def func1(): global num for i in range(100000): num -= 1 def func2(): global num for i in range(100000): num += 1 for i in range (10): # 启动线程1 t1 = Thread(target=func1) t1.start() # 启动线程2 t2 = Thread(target=func2) t2.start() lst.append(t1) lst.append(t2) for i in lst: i.join() print("主线程执行结束") print(num)
执行
由于共享同一份,会出现数据混乱,如下结果
[root@node10 python]# python3 test.py 主线程执行结束 173624 [root@node10 python]# python3 test.py 主线程执行结束 131875 [root@node10 python]# python3 test.py 主线程执行结束 -4279 [root@node10 python]# python3 test.py 主线程执行结束 -93587 [root@node10 python]# python3 test.py 主线程执行结束 145643
这是因为线程是并发执行,同同一时间会有多个线程拿到同一个数据进行计算,然后再放回去,导致一个数字被多个线程修改,结果不准确
2 引入锁机制
from threading import Thread,Lock num = 0 lst = [] def func1(lock): global num for i in range(100000): #上锁修改数据 lock.acquire() num -= 1 #解锁释放 lock.release() def func2(lock): global num for i in range(100000): #使用with,自动上锁和解锁 with lock: num += 1 lock = Lock() for i in range (10): # 启动线程1 t1 = Thread(target=func1,args=(lock,)) t1.start() # 启动线程2 t2 = Thread(target=func2,args=(lock,)) t2.start() lst.append(t1) lst.append(t2) for i in lst: i.join() print("主线程执行结束") print(num)
执行
[root@node10 python]# python3 test.py 主线程执行结束 0
结果正确,但是消耗的时间毕竟长,但是可以换取数据正确
3 信号量
import time,random,os def func(i,sem): time.sleep(random.uniform(0.1,1)) with sem: print (i) print (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) time.sleep(random.uniform(3,6)) sem = Semaphore(5) for i in range (20): Thread(target = func,args=(i,sem)).start()
执行
[root@node10 python]# python3 test.py 3 2020-02-23 03:55:24 5 2020-02-23 03:55:24 14 2020-02-23 03:55:24 18 2020-02-23 03:55:24 6 2020-02-23 03:55:24 0 2020-02-23 03:55:27 2 2020-02-23 03:55:29 15 2020-02-23 03:55:29 1 2020-02-23 03:55:29 19 2020-02-23 03:55:30 12 2020-02-23 03:55:32 4 2020-02-23 03:55:32 17 2020-02-23 03:55:34 16 2020-02-23 03:55:34 13 2020-02-23 03:55:35 7 2020-02-23 03:55:36 10 2020-02-23 03:55:36 8 2020-02-23 03:55:39 9 2020-02-23 03:55:39 11 2020-02-23 03:55:40
信号量就相当于同时可以上多把锁
死锁,递归锁,互斥锁
4 死锁现象
模拟一个俱乐部玩枪,两把枪,两盒子弹,四个人同时抢,只有同时抢到子弹和枪的人,才能玩枪
初步逻辑
from threading import Thread,Lock import time #首先创建两把锁 gun = Lock() bullet = Lock() #定义第一种,先抢到枪,再抢到子弹 def play1(name): #抢到枪,上锁 gun.acquire() print("%s拿到枪"%(name)) #抢到子弹上锁 bullet.acquire() print ("%s抢到子弹"%(name)) print ("开枪玩一会") time.sleep(0.5) #放下枪,解锁 gun.release() print("%s放下枪"%(name)) #放下子弹,解锁 bullet.release() print("%s放下子弹"%(name)) #定义第二种,先抢到子弹,再抢到枪 def play2(name): #抢到子弹上锁 bullet.acquire() print ("%s抢到子弹"%(name)) #抢到枪,上锁 gun.acquire() print("%s拿到枪"%(name)) print ("开枪玩一会") time.sleep(0.5) #放下子弹,解锁 bullet.release() print("%s放下子弹"%(name)) #放下枪,解锁 gun.release() print("%s放下枪"%(name)) name_lst1 = ["John","Jim"] name_lst2 = ["Tom","Jerry"] for name in name_lst1: Thread(target=play1,args=(name,)).start() for name in name_lst2: Thread(target=play2,args=(name,)).start()
这种情况就会有死锁现象
多次执行,结果如下
[root@node10 python]# python3 test.py John拿到枪 Tom抢到子弹 #阻塞,是因为John和Tom同时上了枪锁和子弹锁,但是都没有解锁,造成死锁 [root@node10 python]# python3 test.py John拿到枪 John抢到子弹 开枪玩一会 John放下枪 John放下子弹 Jim拿到枪 Tom抢到子弹 #阻塞,这次是John顺利的玩一局,但是到Jim和Tom同时抢到枪和子弹,上锁,但是没有解锁,造成死锁 [root@node10 python]# python3 test.py John拿到枪 John抢到子弹 开枪玩一会 John放下枪 John放下子弹 Tom抢到子弹 Jim拿到枪 #阻塞
5 递归锁介绍
上几把锁,解几把锁
from threading import Thread,RLock rlock = RLock() def func(name): rlock.acquire() print(name,1) rlock.acquire() print(name,2) rlock.acquire() print(name,3) rlock.release() rlock.release() rlock.release() for i in range(10): t = Thread(target=func,args=("name%s" % (i),) ) t.start() print("程序执行结束")
执行
[root@node10 python]# python3 ceshi.py name0 1 name0 2 name0 3 name1 1 name1 2 name1 3 name2 1 name2 2 name2 3 name3 1 name3 2 name3 3 程序执行结束 name4 1 name4 2 name4 3 name5 1 name5 2 name5 3 name6 1 name6 2 name6 3 name7 1 name7 2 name7 3 name8 1 name8 2 name8 3 name9 1 name9 2 name9 3
6 利用递归锁,解决死锁现象
临时用于快速解决服务器崩溃死锁的问题,用递归锁应急问题
from threading import Thread,RLock import time #首先创建递归锁 gun=bullet = RLock() #定义第一种,先抢到枪,再抢到子弹 def play1(name): #抢到枪,上锁 gun.acquire() print("%s拿到枪"%(name)) #抢到子弹上锁 bullet.acquire() print ("%s抢到子弹"%(name)) print ("开枪玩一会") time.sleep(0.5) #放下枪,解锁 gun.release() print("%s放下枪"%(name)) #放下子弹,解锁 bullet.release() print("%s放下子弹"%(name)) #定义第二种,先抢到子弹,再抢到枪 def play2(name): #抢到子弹上锁 bullet.acquire() print ("%s抢到子弹"%(name)) #抢到枪,上锁 gun.acquire() print("%s拿到枪"%(name)) print ("开枪玩一会") time.sleep(0.5) #放下子弹,解锁 bullet.release() print("%s放下子弹"%(name)) #放下枪,解锁 gun.release() print("%s放下枪"%(name)) name_lst1 = ["John","Jim"] name_lst2 = ["Tom","Jerry"] for name in name_lst1: Thread(target=play1,args=(name,)).start() for name in name_lst2: Thread(target=play2,args=(name,)).start()
执行
[root@node10 python]# python3 test.py John拿到枪 John抢到子弹 开枪玩一会 John放下枪 John放下子弹 Jim拿到枪 Jim抢到子弹 开枪玩一会 Jim放下枪 Jim放下子弹 Tom抢到子弹 Tom拿到枪 开枪玩一会 Tom放下子弹 Tom放下枪 Jerry抢到子弹 Jerry拿到枪 开枪玩一会 Jerry放下子弹 Jerry放下枪
执行成功,神奇
7 使用互斥锁解决
从语法上来看,锁是可以互相嵌套的,但是不要使用
上一次锁,就对应解开一把锁,形成互斥锁
拿枪和拿子弹是同时的,上一把锁就够了,不要分开上锁,也不要去写锁的嵌套,容易死锁
from threading import Thread,Lock import time #首先创建递归锁 mylock = Lock() #定义第一种,先抢到枪,再抢到子弹 def play1(name): #抢到枪和子弹,上锁 mylock.acquire() print("%s拿到枪"%(name)) print ("%s抢到子弹"%(name)) print ("开枪玩一会") time.sleep(0.5) #放下枪,和子弹解锁 print("%s放下枪"%(name)) print("%s放下子弹"%(name)) mylock.release() #定义第二种,先抢到子弹,再抢到枪 def play2(name): #抢到枪子弹上锁 mylock.acquire() print ("%s抢到子弹"%(name)) print("%s拿到枪"%(name)) print ("%s开枪玩一会"%(name)) time.sleep(0.5) #放枪下子弹,解锁 print("%s放下子弹"%(name)) print("%s放下枪"%(name)) mylock.release() name_lst1 = ["John","Jim"] name_lst2 = ["Tom","Jerry"] for name in name_lst1: Thread(target=play1,args=(name,)).start() for name in name_lst2: Thread(target=play2,args=(name,)).start()
执行
[root@node10 python]# python3 test.py John拿到枪 John抢到子弹 开枪玩一会 John放下枪 John放下子弹 Jim拿到枪 Jim抢到子弹 开枪玩一会 Jim放下枪 Jim放下子弹 Tom抢到子弹 Tom拿到枪 Tom开枪玩一会 Tom放下子弹 Tom放下枪 Jerry抢到子弹 Jerry拿到枪 Jerry开枪玩一会 Jerry放下子弹 Jerry放下枪
完成