协程的含义就再也不提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块。因为协程对于操做系统是无感知的,因此其切换须要程序员本身去完成。html
import time def A(): for i in range(100): print("----A---") yield i time.sleep(0.5) def B(c): while True: print("----B---") try: next(c) except StopIteration: break else: time.sleep(0.5) if __name__ == '__main__': a = A() B(a)
上面的例子并无带来代码效率的提升,由于time.sleep()是同步阻塞操做;上例主要是为了说明协程的上下文切换原理。异步
greenlet模块也是程序显性主动控制协程切换,可是对原生作了必定的封装使得切换变得简单一些。async
from greenlet import greenlet import time def test1(gr,g): for i in range(100): print("---A--") gr.switch(g, gr) # 切换到另外一个协程执行 time.sleep(0.5) def test2(gr, g): for i in range(100): print("---B--") gr.switch(g, gr) # gr.throw(AttributeError) time.sleep(0.5) if __name__ == '__main__': # 建立一个协程1 gr1 = greenlet(test1) # 建立一个协程2 gr2 = greenlet(test2) # 启动协程 gr1.switch(gr2, gr1)
greenlet类主要有两个方法:函数
switch:用来切换协程;
throw():用来抛出异常同时终止程序;
gevent是在greenlet的基础上进行封装使得gevent变得更加的易用。
gevent采用了隐式启动事件循环,即在须要阻塞的时候开启一个专门的协程来启动事件循环;
若是一个任务没有io操做,那么他会一直执行直到完成;其余协程没有执行的机会;
自动识别io事件,放弃CPU控制时间;
# 一个补丁patch_all,注意要放在全部的import前面,其会将线程、进程替换成gevent框架,使得咱们能够用同步编程的方式编写异步代码 from gevent import monkey;monkey.patch_all() import gevent import requests def target0(n): print('--start---{}'.format(n)) res = requests.get('http://www.baidu.com') print(res) return n if __name__ == '__main__': jobs = [gevent.spawn(target0, 1),gevent.spawn(target0, 2),gevent.spawn(target0, 3)] gevent.joinall(jobs) print([job.value for job in jobs])
gevent.spawn():建立一个普通的Greenlet对象并切换; gevent.spawn_later(seconds=3) # 延时建立一个普通的Greenlet对象并切换 gevent.spawn_raw() # 建立的协程对象属于一个组 gevent.getcurrent() # 返回当前正在执行的greenlet gevent.joinall(jobs):将协程任务添加到事件循环,接收一个任务列表 gevent.wait() # 能够替代join函数等待循环结束,也能够传入协程对象列表 gevent.kill() # 杀死一个协程 gevent.killall() # 杀死一个协程列表里的全部协程 monkey.patch_all():很是重要,会自动将python的一些标准模块替换成gevent框架
# 手动设置CPU密集型最大执行时间,若是是单线程的协程不须要关注这个 sys.setcheckinterval(n):每n条执行尝试进行线程切换,n必须是int sys.getswitchinterval() # 默认5ms切换
# Greenlet对象 from gevent import Greenlet # Greenlet对象建立 job = Greenlet(target0, 3) Greenlet.spawn() # 建立一个协程并启动 Greenlet.spawn_later(seconds=3) # 延时启动 # 协程启动 job.start() # 将协程加入循环并启动协程 job.start_later(3) # 延时启动 # 等待任务完成 job.join() # 等待任务完成 job.get() # 获取协程返回的值 # 任务中断和判断任务状态 job.dead() # 判断协程是否死亡 job.kill() # 杀死正在运行的协程并唤醒其余的协程,这个协程将不会再执行,能够 job.ready() # 任务完成返回一个真值 job.successful() # 任务成功完成返回真值,不然抛出错误 # 获取属性 job.loop # 时间循环对象 job.value # 获取返回的值 # 捕捉异常 job.exception # 若是运行有错误,获取它 job.exc_info # 错误的详细信息 # 设置回调函数 job.rawlink(back) # 普通回调,将job对象做为回调函数的参数 job.unlink() # 删除回调函数 # 执行成功的回调函数 job.link_value(back) # 执行失败的回调函数 job.link_exception(back)
经过设置协程池Pool来限制运行的协程的最大数目。该Pool和multiprocessing进程模块的Pool的API十分类似。
Group类管理一组不限制数目的协程对象,可是Pool是它的子类,使用通常用Pool替代。
def tag(): print('--start---') x = requests.get('http://www.baidu.com') print(x) print('------end--------') return 0 if __name__ == '__main__': from gevent.pool import Pool p = Pool(5) for i in range(10): p.apply_async(tag) p.join()
pool.wait_available():等待直到有一个协程有结果 pool.dd(greenlet):向进程池添加一个方法并跟踪,非阻塞 pool.discard(greenlet):中止跟踪某个协程 pool.start(greenlet):加入并启动协程 pool.join():阻塞等待结束 pool.kill():杀死全部跟踪的协程 pool.killone(greenlet):杀死一个协程