python学习day39笔记
作者:互联网
死锁
指两个或两个以上的进程或线程在执行过程中,因争夺资源而导致的一种互相等待的现象,若无外力作用,他们都将无法推进下去
此时称系统处于死锁状态,这些永远在互相等待的进程称为死锁进程
死锁典型问题
科学家吃面问题
一桌科学家吃面,桌上有两个叉子,吃面需要两个叉子,一个科学家抢到叉子等面,一个科学家抢到面等叉子,僵持不下,这就是死锁
from threading import Thread,Lock
import time
def eat1(lock1,lock2,name):
lock1.acquire()
print(f'{name}抢到了叉子')
time.sleep(1)
lock2.acquire()
print(f'{name}抢到了面')
time.sleep(2)
print('吃饱了')
lock2.release()
lock1.release()
def eat2(lock1,lock2,name):
lock2.acquire()
print(f'{name}抢到了面')
time.sleep(1)
lock1.acquire()
print(f'{name}抢到了叉子')
time.sleep(2)
print('吃饱了')
lock1.release()
lock2.release()
if __name__ == '__main__':
lock1 = Lock()
lock2 = Lock()
for i in ['liyang','egon','jason']:
t1 = Thread(target=eat1,args=(lock1,lock2,i))
t1.start()
for i in ['zk','wmy','hjj']:
t2 = Thread(target=eat2,args=(lock1,lock2,i))
t2.start()
解决方法
用递归锁解决死锁问题
from threading import RLock
if __name__ == '__main__':
lock1 = RLock()
lock2 = lock1
for i in ['liyang','egon','jason']:
t1 = Thread(target=eat1,args=(lock1,lock2,i))
t1.start()
for i in ['zk','wmy','hjj']:
t2 = Thread(target=eat2,args=(lock1,lock2,i))
t2.start()
这个RLock内部维护着一个Lock锁和一个counter变量,counter记录了acquire的次数,如果counter不为0,则其他线程无法获得锁,从而使得线程可以连续多次require
线程队列queue
queue队列:使用 import queue ,用法与进程Queue一样
在同一个进程下多个线程数据是共享的,之所以使用队列是因为队列是管道+锁,为了保证数据的安全才使用队列
先进先出
from queue import Queue
if __name__ == '__main__':
q = Queue()
q.put('ly dsb')
q.put('我最帅')
print(q.get())
# ly dsb
先进后出
栈的排列方式
from queue import Queue
if __name__ == '__main__':
q = LifoQueue()
q.put('ly dsb')
q.put('我最帅')
print(q.get())
# 我最帅
优先级队列
from queue import PriorityQueue
if __name__ == '__main__':
q = PriorityQueue()
q.put((20,'ly dsb'))
q.put((10,'我最帅'))
# put传入一个元组,原则的第一个元素是优先级,通常是数字,也可以是非数字之间的比较,数字越小在队列内优先级越高
print(q.get())
# (10, '我最帅')
协程
协程基础
协程在线程内,线程在进程内
协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine
协程是一种用户态的轻量级线程,即协程是有用户程序自己控制调度的
强调1:python的线程属于内核级别,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
强调2:单线程内开启协程,一旦遇到io就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(非io操作的切换与效率无关)
对比操作系统控制线程的切换,用户在单线程内控制协程的切换的优缺点
优点1:协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
优点2:单线程内就可以实现并发的效果,最大限度的利用cpu
缺点1:协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程
缺点2:协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
总结协程特点:
1.必须在只有一个单线程里实现并发
2.修改共享数据不需加锁
3.用户程序里自己保存多个控制流的上下文栈
4.附加:一个协程遇到io操作自动切换到其他协程(如何实现检测io,yield、greenlet都无法实现,就用到了gevent模块(select机制))
greenlet模块
首先安装模块:pip3 install greenlet
from greenlet import greenlet
def num1():
print('1')
g2.switch()
print('2')
def num2():
print('3')
g1.switch()
print('4')
g1 = greenlet(num1)
g2 = greenlet(num2)
g1.switch()
# 1
# 3
# 2
greenlet就是用来在线程之间来回切换运行,切换后暂停之前运行的任务
gevent模块
首先安装模块:pip install gevent
import gevent
def num1():
print('1')
# time.sleep(2)
gevent.sleep(2)
print('2')
def num2():
print('3')
# time.sleep(2)
gevent.sleep(2)
print('4')
g1 = gevent.spawn(num1)
g2 = gevent.spawn(num2)
g1.join()
g2.join()
# 1
# 3
# 2
# 4
gevent会自动捕捉到gevent的io操作,然后切换进程
如果非要使用time模块还要切换线程,加上猴子补丁:
from gevent import monkey
monkey.patch_all()
标签:__,协程,day39,python,笔记,lock2,lock1,线程,print 来源: https://www.cnblogs.com/Milu-Mi/p/15050314.html