# 借助生成器实现 两个子函数的并发 import time def task1(): while True: print("---1---") time.sleep(1) yield def task2(): while True: print("---2---") time.sleep(1) yield def main(): t1 = task1() t2 = task2() while True: next(t1) next(t2) if __name__ == "__main__": main()
协程的原理就是上一讲的生成器,而且只有一个线程,因此按照所需时间和内存成本:进程>线程>协程。
greenlet
实现多任务我们可以使用greenlet.greenlet
代替协程中的yield
,实现两个任务的并发
from greenlet import greenlet import time def test1(): print("---1---") time.sleep(1) gr2.switch() def test2(): print("---2---") time.sleep(1) gr2.switch() gr1 = greenlet(test1) gr2 = greenlet(test2) # 切换到gr1中执行 gr1.switch()
但是一般不用greenlet
而是用更方便的gevent
gevent
实现多任务[重点]相比需要手动切换的greenlet
,gevent
遇到延时时可以自动切换,更加智能。
obj = gevent.spawn(function,args)
创建协程但不立即执行;obj.join()
开始执行协程,并根据延时情况,自动切换;而gevent.sleep()
能够代替上面的time.sleep()
,实现延时控制。
import gevent def f1(n): for i in range(n): print(gevent.getcurrent(),i) # time.sleep(0.5) 不管用 gevent.sleep(0.5) # 协程的核心在于,利用空闲的时间执行其他协程 def f2(n): for i in range(n): print(gevent.getcurrent(),i) gevent.sleep(0.5) def f3(n): for i in range(n): print(gevent.getcurrent(),i) gevent.sleep(0.5) print("---1---") g1 = gevent.spawn(f1,5) # 注意:创建之后并不执行 print("---2---") g2 = gevent.spawn(f2,5) print("---3---") g3 = gevent.spawn(f3,5) # gevent比greenlet更加智能 # 因为遇到了延时,gevent可以自动切换 g1.join() g2.join() g3.join()
如果还想继续使用time.sleep()
来控制延时的话,可以在文件开头加入以下代码,gevent就能自动检测延时:
from gevent import monkey monkey.patch_all()
主函数中每个协程都需要通过.join()
方法启动有点麻烦,可以使用gevent.joinall()
,将所有要启动的协程排成列表传入,例如:
import gevent,time from gevent import monkey monkey.patch_all() def func(): pass gevent.joinall([ gevent.spawn(func,"work1"), gevent.spawn(func,"work2") ])
import urllib.request import gevent from gevent import monkey monkey.patch_all() def downloader(img_name,img_url): req = urllib.request.urlopen(img_url) img_content = req.read() with open(img_name+'.jpg','wb') as f: f.write(img_content) def main(): gevent.joinall([ gevent.spawn(downloader,'1','https'), gevent.spawn(downloader,'2','https') ]) if __name__ == "__main__": main()