044.Python线程的数据安全
线程的数据安全
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放下枪
完成
学习记录,小白一枚


浙公网安备 33010602011771号