队列,死锁,进程同步(Python day 21)
锁
线程中的锁是为了让线程有序的执行而不会因为抢夺资源发生错误
from threading import Thread,Lock
import time
dongzuo=0
def sing():
global dongzuo
for i in range(5):
lockx.acquire() #关锁
print('唱歌:动作',dongzuo)
dongzuo+=1
lockx.release() #开锁
time.sleep(0.6)
def dance():
global dongzuo
for i in range(5):
lockx.acquire() #开锁
print('跳舞:动作',dongzuo)
dongzuo+=1
lockx.release() #关锁
time.sleep(0.6)
if __name__ == '__main__':
#没有锁的时候会形成线程抢夺资源的情况
lockx=Lock() #这里是否能设置时间形成死锁,答:不行
t1=Thread(target=sing)
t2=Thread(target=dance)
t1.start()
t2.start()
这是没有加锁的情况,可以看到两个线程的进行情况是不可控的
这是加锁的结果,两个进程还是会抢夺资源,但是i已经按照预期结果走了下去
死锁
死锁是因为多个进程互相等待对方结束而产生的结果
轻者宕机,重者监护室
from threading import Thread,Lock
import time
def suo1():
suo_1.acquire()
print('锁1还差一个锁2的必要条件')
time.sleep(1) #形成死锁的一个原因还有时间
suo_2.acquire()
print('锁1拿到了全部条件,开始执行')
suo_2.release()
suo_1.release()
def suo2():
suo_2.acquire()
print('锁2还差一个锁1必要条件')
time.sleep(1) #这个时间很重要
suo_1.acquire()
print('锁2拿到了全部条件,开始执行')
suo_1.release()
suo_2.release()
if __name__ == '__main__':
suo_1=Lock()
suo_2=Lock()
sisuo_1=Thread(target=suo1)
sisuo_2=Thread(target=suo2)
sisuo_1.start()
sisuo_2.start()
如图所示(黄色箭头位置),这个程序会一直占有cup,即一直处于用户态,当运行时间过长时,cup会因过热而发生故障
形成死锁的一个原因还有时间
队列 (队列是不可置换的)
另一种方式调出队列
这样不用经过线程的模块
from queue import Queue
q=Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.empty())
print(q.full())
print(q.get())
q.put(4)
try:
q.put(5)
except:
print('加不进去')
LifoQueue-------------栈(一端受限制的队列)
from queue import LifoQueue
q=LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
栈,先进后出
优先队列————伪队列
之所以称之为,伪队列,是因为它不属于队列,因为你自身排序了,不符合队列的不可置换性
from queue import PriorityQueue
q=PriorityQueue(5)#必须是元组,并且是按照元组的第一个顺序开始比较
q.put((2,'消息1'))
q.put((3,'消息2'))
q.put((5,'消息3'))
q.put((1,'消息4'))
print(q.get())
print(q.get())
print(q.get())
print(q.get())
while q.qsize():
print(q.get())
线程之间的通信
from threading import Thread,Lock
import time
from queue import Queue
def bao(name,q):
count=0
while True:
bz='包子%d'%(count+1)
q.put(bz)
s=name+'包'+bz
count+=1
print(s)
time.sleep(1)
def chi(name,q):
while True:
bz=q.get()
s=name+'吃'+bz
print(s)
time.sleep(1.5)
if __name__ == '__main__':
q=Queue(3)
name='cheng'
t1=Thread(target=bao,args=(name,q))
t2=Thread(target=chi,args=('ooooo',q))
t1.start()
t2.start()
线程之间的通信问题是靠用线程函数将参数和队列传入到上面的函数中
重要的是将队列传入,是因为队列才是让两个函数通信的关键 “信使”
重载方式写——进程子类化 通信
from threading import Thread
from queue import Queue
import time
class MyThread(Thread):
def __init__(self,q,style):
super().__init__()
self.q=q
self.style=style
def run(self):
if self.style=='生产':
i=0
while i<10:
self.q.put(str('包子'+str(i+1)))
s='生成包子'+str(i+1)
print(s)
time.sleep(1)
i+=1
else:
i=0
while i<10:
s='消费'+q.get()
print(s)
time.sleep(1)
i+=1
if __name__ == '__main__':
q=Queue(3)
t1=MyThread(q,'生产')
t1.start()
t2=MyThread(q,'xiaofei')
t2.start()
虽然是并发的,但是因为队列使得并发需要等待
线程同步
同步-----------协同步调
from threading import Thread,Lock
x=0
def A():
global x
i=0
l.acquire()#※
while i<100000000:
x+=1
i+=1
l.release()#※
print(x)
def B():
global x
l.acquire()#※
i=0
while i<100000000 :#数字很大时会出错
x+=1
i+=1
l.release()#※
print(x)
if __name__ == '__main__':
l=Lock()#※
t1=Thread(target=A)
t2=Thread(target=B)
t1.start()
t2.start()
这样通过加入锁的概念,使得线程变的有序了起来
协程
协程用的时间和资源消耗比进程切换小的多
什么是协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
import time
def A(hs):
i=0
while i<3:
print('A,开始')
next(hs) #next只是把里面的函数再执行一遍,最精彩的是这个yield
#next有启动功能
print('A,结束')
i+=1
def b():
i=0
while i<3:
print('B,开始')
yield #暂时挂起
print('B,结束')
if __name__ == '__main__':
x=b()
A(x)
由下图框框可以看出,这个yield形成的效果是完全符合栈的特点(栈具有可存储的功能)
这样就形成了一个协程的概念