1.协程
作者:互联网
协程
协程不是计算机提供,而是人为创造出来的
协程也可以被称为微线程,是一种用户态内的上下文切换技术,其实就是通过一个线程实现代码块相互切换执行
协程本质上是一个线程上多个代码来回切换执行
协程实现的几种方法:
greenlet:早期的一个实现协程的第三方模块
gevent: 基于greenlet实现的协程,python第三方库
yield关键字
asyncio装饰器 (版本>=py3.4)
async、await关键字 (版本>=3.5,推荐使用)
greenlet实现协程
创建一个greenlet时,初始化一个空的栈, switch到这个栈的时候,
会运行在greenlet构造时传入的函数
#安装
pip3 install greenlet
'''比较重要的几个属性'''
switch() #协程切换
getcurrent() #返回当前的greenlet实例
GreenletExit #是一个特殊的异常,当触发了这个异常的时候,即使不处理,也不会抛到其parent
run #当greenlet启动的时候会调用到这个callable,如果我们需要继承greenlet.greenlet时,需要重写该方法
parent #可读写属性
dead #如果greenlet执行结束,那么该属性为true
throw #切换到指定greenlet后立即抛出出异常
# 导入
from greenlet import greenlet
def func1():
print(1) # 第二步: 输出1
gr2.switch() # 第三步: 切换到func2函数
print(2) # 第六步:输出2
gr2.switch() # 第七步: 切换到func2函数
def func2():
print(3) # 第四步:输出4
gr1.switch() # 第五步:切换到func1函数
print(4) # 第八步:输出4
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 第一步 执行func1函数
gevent实现协程
#安装
pip3 install gevent
g1 = gevent.spawn(func,param1,param2) #创建一个g1协程对象,第一个参数是函数名,后面是函数带的参数
g1.join() #等待协程执行
g1.value #拿到方法的返回值
gevent.sleep(s) #IO阻塞指定时间时,切换到其他协程
gevent.joinall([g1,g2]]) # 批量join,可以传入多个方法,不用一个一个join
gevent.queue.Queue # 协程安全队列
gevent.spawn 启动的所有协程,都是运行在同一个线程之中,所以协程不能跨线程同步数据。
gevent 启动的并发协程,具体到任务方法,不能有长时间阻塞的IO操作。因为gevent的协程的特点是,当前协程阻塞了才会切换到别的协程。如果当前协程长时间阻塞,则不能切换到别的协程。导致程序出问题。
如果有长时间阻塞的 IO 操作,还是用传统的线程模型比较好。
gevent 适合处理大量无阻塞的任务,如果有实在不能把阻塞的部分变为非阻塞再交给 gevent 处理,就把阻塞的部分改为异步
默认情况下time.sleep()不能被gevent识别为耗时操作
1.把time.sleep()替换为gevent.sleep()
2.添加补丁使gevent可以识别time.sleep()
monkey补丁的作用
1.在运行时替换方法、属性等
2.在不修改第三方代码的情况下增加原来不支持的功能
3.在运行时为内存中的对象增加patch,而不是在磁盘的源代码中增加
gevent
gevent.spawn(func,value1,value2,......)
功能:创建gevent对象
参数:
target:协程执行的函数名
gevent.getcurrent()
功能:获取当前协程对象
gevent.joinall([args])
功能:批量为协程执行join()
参数:需要join()的gevent组成的list
from gevent import monkey
monkey.patch_all()
功能:破解所有,使gevent可以识别time.sleep()
obj.sleep(time)
功能:休眠,是gevent可以识别的time.sleep()
obj.join()
功能:造成阻塞,使主线程等待协程执行完成再结束
#示例1
from gevent import monkey
monkey.patch_all()
# 导入 gevent
import gevent
def work1():
print('work1')
gevent.sleep(1)
print('end1')
def work2():
print('work2')
gevent.sleep(1)
print('end2')
if __name__ == '__main__':
g1 = gevent.spawn(work1)
g2 = gevent.spawn(work2)
# 造成阻塞,阻塞时自动切换其他
g1.join()
g2.join()
#示例2-协程池
# gevent的猴子补丁
from gevent import monkey
# 导入协程池
from gevent.pool import Pool
import gevent
monkey.patch_all()
def num(n):
print(n * n)
return n * n
if __name__ == '__main__':
# 实例化一个协程池,参数为最大协程数
pool = Pool(10)
# 生成协程列表,
p = [pool.spawn(num, i) for i in range(10)]
gevent.joinall(p)
yield关键字实现协程
def func1():
yield 1 # 第一步:返回1
yield from func2() # 第二步: 切换到func2
yield 2 # 第五步:返回2
def func2():
yield 3 # 第三步:返回3
yield 4 # 第四步:返回4
f1 = func1() # 返回一个生成器
for item in f1:
print(item)
asyncio实现协程
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(2) #遇到IO耗时操作,自动切换到tasks中的其他任务
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2) #遇到IO耗时操作,自动切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# 遇到IO阻塞自动切换
async & await关键字实现协程-推荐使用
import asyncio
async def func1():
print(1)
await asyncio.sleep(2) # 遇到IO耗时操作,自动切换到tasks中的其他任务
print(2)
async def func2():
print(3)
await asyncio.sleep(2) # 遇到IO耗时操作,自动切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# 遇到IO阻塞自动切换
标签:协程,gevent,greenlet,print,sleep,asyncio 来源: https://www.cnblogs.com/Mickey-7/p/16316007.html