线程间的资源竞争
#!/user/bin/env python
#-*-coding utf-8-*-
#@Time : 2020/6/158:44
#@Author : GodSpeed
#@File : 线程的资源竞争.py
#@Software : PyCharm
import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))
g_num = 0
def demo1(args):
global g_num
print(args+'---------开始运行-------------'+str(g_num))
for i in range(50*100000):
g_num += 1
#print("I am demo1---{} g_num={}次运行\n".format(i,g_num))
print(args + '-----------运行结束-----------'+str(g_num))
def demo2(args):
global g_num
print(args+'---------开始运行-------------'+str(g_num))
for i in range(30*100000):
g_num += 1
#print("I am demo2---{} g_num={}次运行\n".format(i,g_num))
print(args + '-----------运行结束-----------'+str(g_num))
print('\n')
def main():
t1 = threading.Thread(target=demo1,args=("demo1",))
t2 = threading.Thread(target=demo2,args=("demo2",))
t1.start()
t2.start()
if __name__ == '__main__':
main()
结果:
demo1---------开始运行-------------0
demo2---------开始运行-------------97535
demo2-----------运行结束-----------3431958
demo1-----------运行结束-----------5370013
问题:
线程1和线程2出现了资源强制,导致num并没有有序的增加,缺失了很多数据
互斥锁和死锁
某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程 不能改变,只到该线程释放资源,将资源的状态变成"⾮锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正确性。
#!/user/bin/env python
#-*-coding utf-8-*-
#@Time : 2020/6/158:44
#@Author : GodSpeed
#@File : 线程的资源竞争.py
#@Software : PyCharm
import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))
#创建锁
mutex = threading.Lock()
g_num = 0
def demo1(args):
global g_num
#上锁
mutex.acquire()
print(args+'---------开始运行-------------'+str(g_num))
for i in range(50*100000):
g_num += 1
#print("I am demo1---{} g_num={}次运行\n".format(i,g_num))
# 解锁
mutex.release()
print(args + '-----------运行结束-----------'+str(g_num))
def demo2(args):
global g_num
# 上锁
mutex.acquire()
print(args+'---------开始运行-------------'+str(g_num))
for i in range(30*100000):
g_num += 1
#print("I am demo2---{} g_num={}次运行\n".format(i,g_num))
# 解锁
mutex.release()
print(args + '-----------运行结束-----------'+str(g_num))
def main():
t1 = threading.Thread(target=demo1,args=("demo1",))
t2 = threading.Thread(target=demo2,args=("demo2",))
t1.start()
t2.start()
if __name__ == '__main__':
main()
结果:
demo1---------开始运行-------------0
demo1-----------运行结束-----------5000000
demo2---------开始运行-------------5000000
demo2-----------运行结束-----------8000000
多重锁
#!/user/bin/env python
#-*-coding utf-8-*-
#@Time : 2020/6/158:44
#@Author : GodSpeed
#@File : 线程的资源竞争.py
#@Software : PyCharm
import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))
#创建锁
mutex = threading.Lock()
mutex1 = threading.Lock()
g_num = 0
def demo1(args):
global g_num
#上锁
mutex.acquire()
#mutex1.acquire()
mutex.acquire()
print(args+'---------开始运行-------------'+str(g_num))
for i in range(50*100000):
g_num += 1
#print("I am demo1---{} g_num={}次运行\n".format(i,g_num))
#mutex1.release()
# 解锁
mutex.release()
mutex.release()
print(args + '-----------运行结束-----------'+str(g_num))
def demo2(args):
global g_num
# 上锁
mutex.acquire()
print(args+'---------开始运行-------------'+str(g_num))
for i in range(30*100000):
g_num += 1
#print("I am demo2---{} g_num={}次运行\n".format(i,g_num))
# 解锁
mutex.release()
print(args + '-----------运行结束-----------'+str(g_num))
def main():
t1 = threading.Thread(target=demo1,args=("demo1",))
t2 = threading.Thread(target=demo2,args=("demo2",))
t1.start()
t2.start()
if __name__ == '__main__':
main()
结果
同一锁名,多重上锁,threading.Lock无法释放
解决方法,用RLock
#!/user/bin/env python
#-*-coding utf-8-*-
#@Time : 2020/6/158:44
#@Author : GodSpeed
#@File : 线程的资源竞争.py
#@Software : PyCharm
import threading
import time
import dis #查看Python代码在cpu的运行轨迹
##print(dis.dis(fun))
#创建锁
#mutex = threading.Lock()
mutex = threading.RLock()
mutex1 = threading.Lock()
g_num = 0
def demo1(args):
global g_num
#上锁
mutex.acquire()
#mutex1.acquire()
mutex.acquire()
print(args+'---------开始运行-------------'+str(g_num))
for i in range(50*100000):
g_num += 1
#print("I am demo1---{} g_num={}次运行\n".format(i,g_num))
#mutex1.release()
# 解锁
mutex.release()
mutex.release()
print(args + '-----------运行结束-----------'+str(g_num))
def demo2(args):
global g_num
# 上锁
mutex.acquire()
print(args+'---------开始运行-------------'+str(g_num))
for i in range(30*100000):
g_num += 1
#print("I am demo2---{} g_num={}次运行\n".format(i,g_num))
# 解锁
mutex.release()
print(args + '-----------运行结束-----------'+str(g_num))
def main():
t1 = threading.Thread(target=demo1,args=("demo1",))
t2 = threading.Thread(target=demo2,args=("demo2",))
t1.start()
t2.start()
if __name__ == '__main__':
main()
结果
demo1---------开始运行-------------0
demo1-----------运行结束-----------5000000
demo2---------开始运行-------------5000000
demo2-----------运行结束-----------8000000
死锁
在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时等
待对⽅的资源,就会造成死锁。
#!/user/bin/env python
#-*-coding utf-8-*-
#@Time : 2020/6/1513:24
#@Author : GodSpeed
#@File : 死锁.py
#@Software : PyCharm
import threading
import time
class MyThread1(threading.Thread):
def run(self):
# 对mutexA上锁
mutexA.acquire()
# mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
print(self.name+'----do1---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
mutexB.acquire()
print(self.name+'----do1---down----')
mutexB.release()
# 对mutexA解锁
mutexA.release()
class MyThread2(threading.Thread):
def run(self):
# 对mutexB上锁
mutexB.acquire()
# mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
print(self.name+'----do2---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
mutexA.acquire()
print(self.name+'----do2---down----')
mutexA.release()
# 对mutexB解锁
mutexB.release()
mutexA = threading.Lock()
mutexB = threading.Lock()
if __name__ == '__main__':
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
结果
Thread-1----do1—up----
Thread-2----do2—up----
被锁住,无法解锁
Queue线程
在线程中,访问⼀些全局变量,加锁是⼀个经常的过程。如果你是想把⼀些数
据存储到某个队列中,那么Python内置了⼀个线程安全的模块叫做queue模 块。Python中的queue模块中提供了同步的、线程安全的队列类,包括 FIFO(先进先出)队列Queue,LIFO(后⼊先出)队列LifoQueue。这些队列
都实现了锁原语(可以理解为原⼦操作,即要么不做,要么都做完),能够在
多线程中直接使⽤。可以使⽤队列来实现线程间的同步。
#Queue 对数据结构中的栈和队列的封装 先进后出
from queue import Queue
qu = Queue(3) #创建⼀个先进先出的队列。
#qsize() # 返回队列的⼤⼩。
print(qu.qsize())
#empty() # 判断队列是否为空。
if qu.empty():
print('队列为空')
#put() # 将⼀个数据放到队列中。
qu.put([1,2])
qu.put([3,4])
qu.put([5,6])
print(qu.full())
if qu.full():
print('队列已满,无法添加')
else:
qu.put(['a','b','c','d','e','f']) #如果不加队列是否满的加判断超过队列的size会出现堵塞
#也可以设置超时时间
#qu.put(1,timeout=2) #报错queue.Full
#qu.put_nowait(3) #报错queue.Full
print('---------------------------')
#get() # 从队列中取最后⼀个数据。
print(qu.get())
qu.put_nowait(3)
print(qu.get())
print(qu.get())
print(qu.get())
#print(qu.get()) 获取次数,超过队列大小,会阻塞
#print(qu.get_nowait()) #超过范围直接报错 _queue.Empty
线程同步实例
方式一:
# !/user/bin/env python
# -*-coding utf-8-*-
# @Time : 2020/6/1515:42
# @Author : GodSpeed
# @File : condition.py
# @Software : PyCharm
import threading
from threading import Condition
class Question(threading.Thread):
def __init__(self, condition):
super().__init__(name='提问')
self.condition = condition
def run(self) -> None:
self.condition.acquire()
print('A1问:什么叫见过大世面?')
self.condition.notify()
self.condition.wait()
print('A2问:怎样控制住自己不发脾气?')
self.condition.notify()
self.condition.wait()
print('A3问:太在乎自己在别人眼中的形象,导致活得很累,怎么办?')
self.condition.notify()
self.condition.wait()
print('A4问:刚刚看到有人说中国有50个城市的白领平均月薪达到了8730元?')
self.condition.notify()
self.condition.wait()
print('A5问:吃什么最补脑?')
self.condition.notify()
self.condition.wait()
print('A6问:你怎么一个人出来逛街?')
self.condition.notify()
self.condition.release()
class Answer(threading.Thread):
def __init__(self, condition):
super().__init__(name='回答')
self.condition = condition
def run(self) -> None:
self.condition.acquire()
#self.condition.wait()
print('B1神回复:能享受最好的,能承受最坏的。')
self.condition.notify()
self.condition.wait()
print('B2神回复:如果你是对的,你没必要发脾气;如果你是错的,你没资格发脾气。')
self.condition.notify()
self.condition.wait()
print('B3神回复:没有那么多人关注你,所以,放轻松点,你没有那么多观众。')
self.condition.notify()
self.condition.wait()
print('B4神回复 :平均工资能说明什么问题?潘长江和姚明平均身高是196CM,能说明什么?潘长江很高吗?')
self.condition.notify()
self.condition.wait()
print('B5神回复:吃亏。')
self.condition.notify()
self.condition.wait()
print('B6神回复:半个人我怕吓着你啊!')
condition.notify()
self.condition.release()
if __name__ == '__main__':
#lock = threading.RLock()
condition = Condition()
qu = Question(condition)
an = Answer(condition)
qu.start()
an.start()
结果:
A1问:什么叫见过大世面?
B1神回复:能享受最好的,能承受最坏的。
A2问:怎样控制住自己不发脾气?
B2神回复:如果你是对的,你没必要发脾气;如果你是错的,你没资格发脾气。
A3问:太在乎自己在别人眼中的形象,导致活得很累,怎么办?
B3神回复:没有那么多人关注你,所以,放轻松点,你没有那么多观众。
A4问:刚刚看到有人说中国有50个城市的白领平均月薪达到了8730元?
B4神回复 :平均工资能说明什么问题?潘长江和姚明平均身高是196CM,能说明什么?潘长江很高吗?
A5问:吃什么最补脑?
B5神回复:吃亏。
A6问:你怎么一个人出来逛街?
B6神回复:半个人我怕吓着你啊!
注意:调用必须逆序,否则会造成阻塞
方式二:
#!/user/bin/env python
#-*-coding utf-8-*-
#@Time : 2020/6/1516:52
#@Author : GodSpeed
#@File : 线程同步方式二.py
#@Software : PyCharm
# !/user/bin/env python
# -*-coding utf-8-*-
# @Time : 2020/6/1515:42
# @Author : GodSpeed
# @File : condition.py
# @Software : PyCharm
import threading
from threading import Condition
class Question(threading.Thread):
def __init__(self, condition):
super().__init__(name='提问')
self.condition = condition
def run(self) -> None:
#self.condition.acquire()
with self.condition:
print('A1问:什么叫见过大世面?')
self.condition.notify()
self.condition.wait()
print('A2问:怎样控制住自己不发脾气?')
self.condition.notify()
self.condition.wait()
print('A3问:太在乎自己在别人眼中的形象,导致活得很累,怎么办?')
self.condition.notify()
self.condition.wait()
print('A4问:刚刚看到有人说中国有50个城市的白领平均月薪达到了8730元?')
self.condition.notify()
self.condition.wait()
print('A5问:吃什么最补脑?')
self.condition.notify()
self.condition.wait()
print('A6问:你怎么一个人出来逛街?')
self.condition.notify()
#self.condition.release()
class Answer(threading.Thread):
def __init__(self, condition):
super().__init__(name='回答')
self.condition = condition
def run(self) -> None:
#self.condition.acquire()
with self.condition:
self.condition.wait()
print('B1神回复:能享受最好的,能承受最坏的。')
self.condition.notify()
self.condition.wait()
print('B2神回复:如果你是对的,你没必要发脾气;如果你是错的,你没资格发脾气。')
self.condition.notify()
self.condition.wait()
print('B3神回复:没有那么多人关注你,所以,放轻松点,你没有那么多观众。')
self.condition.notify()
self.condition.wait()
print('B4神回复 :平均工资能说明什么问题?潘长江和姚明平均身高是196CM,能说明什么?潘长江很高吗?')
self.condition.notify()
self.condition.wait()
print('B5神回复:吃亏。')
self.condition.notify()
self.condition.wait()
print('B6神回复:半个人我怕吓着你啊!')
condition.notify()
#self.condition.release()
if __name__ == '__main__':
#lock = threading.RLock()
condition = Condition()
qu = Question(condition)
an = Answer(condition)
an.start()
qu.start()
因为Condition类中有
def __enter__(self):
return self._lock.__enter__()
def __exit__(self, *args):
return self._lock.__exit__(*args)
所以可以采用with来代替self.condition.acquire()和self.condition.release()