协程greenlet、gevent、猴子补丁
1.协程:微线程(这个概念是在python中有的)
进程>线程>协程
一个线程里面可以包含多个协程
协程是通过生成器完成的,yield
协程应用场景:
有耗时操作的时候应用
耗时操作:网络请求,网络下载(爬虫),IO操作(本地文件的读写),阻塞
在函数中有yield,函数就变成了生成器,就可以使用next()方法
import time # 在函数中有yield,函数就变成了生成器,就可以使用next()方法 def task1(): for i in range(3): print('A' + str(i)) yield time.sleep(1) def task2(): for i in range(3): print('B' + str(i)) yield time.sleep(2) if __name__ == '__main__': g1 = task1() g2 = task2() while True: try: next(g1) next(g2) except: break
2.使用greenlet完成协程任务
# 使用greenlet完成协程任务 import time from greenlet import greenlet def a(): # 任务A for i in range(5): print('A' + str(i)) gb.switch() time.sleep(0.1) def b(): # 任务B for i in range(5): print('B' + str(i)) gc.switch() time.sleep(0.1) def c(): # 任务C for i in range(5): print('C' + str(i)) ga.switch() time.sleep(0.1) if __name__ == '__main__': ga = greenlet(a) gb = greenlet(b) gc = greenlet(c) ga.switch()
3.gevent和猴子补丁(monkey path)
greenlet已经实现了协程,但是这个人工切换,是不是觉得太麻烦了,不要着急,python还有一个比greenlet更强大的并且能够自动切换任务的模块`gevent`
其原理是当一个greentlet遇到IO(指的是input ouput输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO完成,再适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
猴子补丁,将程序中用到的耗时操作的代码,换为gevent中自己实现的模块,如time.sleep(0.1),gevent中也有sleep()方法
一种在运行时候动态修改类,函数的功能的属性编程技巧
1. 在运行时候替换方法,属性
2. 在不修改第三方代码情况下,增加原来不支持对象
3. 运行时为内存中对象增加PATCH,不是在源代码磁盘中增加
import time import gevent from gevent import monkey # 有耗时操作时需要 monkey.patch_all() # 打补丁,将程序中用到的耗时操作的代码,换为gevent中自己实现的模块 def a(): # 任务A for i in range(5): print('A' + str(i)) time.sleep(0.1) def b(): # 任务B for i in range(5): print('B' + str(i)) time.sleep(0.1) def c(): # 任务C for i in range(5): print('C' + str(i)) time.sleep(0.1) if __name__ == '__main__': g1 = gevent.spawn(a) g2 = gevent.spawn(b) g3 = gevent.spawn(c) g1.join() g2.join() g3.join()
4.案例
import requests import gevent from gevent import monkey monkey.patch_all() def download(url): response = requests.get(url) # 耗时操作 content = response.text print('下载了{}的数据,长度:{}'.format(url, len(content))) if __name__ == '__main__': urls = ['http://www.163.com', 'http://www.qq.com', 'http://www.baidu.com'] g1 = gevent.spawn(download, urls[0]) g2 = gevent.spawn(download, urls[1]) g3 = gevent.spawn(download, urls[2]) g1.join() g2.join() g3.join() # gevent.joinall(g1, g2, g3)