一、协程在python中常用的包
yield、greenlet、gevent、asyncio
二、创建协程
1)使用yield创建协程
import time def consumer(name): # 生成器 print("%s 要开始吃包子了!" % name) while True: baozi=yield # 暂停,记录位置,返回跳出(接收下面send发送的数据,接收到数据后才会继续执行) print("包子%s,%s吃了"%(baozi,name)) def producer(name): c = consumer("消费者") # 只是变成一个生成器 c.__next__() # next只唤醒yield不传递值 for i in range(4): time.sleep(1) print("%s 做了包子%s"%(name,i)) c.send(i) # 发送数据 if __name__ == "__main__": producer("生产者") 结果: 消费者 要开始吃包子了! 生产者 做了包子0 包子0,消费者吃了 生产者 做了包子1 包子1,消费者吃了 生产者 做了包子2 包子2,消费者吃了 生产者 做了包子3 包子3,消费者吃了
2)使用greenlet创建协程
from greenlet import greenlet def proc1(): print(12) gr2.switch() # 执行权切换到gr2,gr1暂停执行 print(34) gr2.switch() # 执行权切换到gr2,gr1将从刚才暂停的地方继续执行 def proc2(): print(56) gr1.switch() # 执行权切换到gr1,gr1将从刚才暂停的地方继续执行,gr2暂停执行 print(78) if __name__ == "__main__": gr1 = greenlet(proc1) # 启动协程1 gr2 = greenlet(proc2) # 启动协程2 gr1.switch() # 先执行gr1指定运行的函数 结果: 12 56 34 78
3)使用gevent创建协程
from gevent import monkey; monkey.patch_all() # 这个代表遇到IO就移交执行权,不用等待 import gevent import requests def f(url): print('GET: %s' % url) resp = requests.get(url) # 网络IO会切换执行权 data = resp.text print('%d bytes received from %s.' % (len(data), url)) gevent.joinall([ gevent.spawn(f, 'https://www.baidu.com/'), gevent.spawn(f, 'https://www.sina.com.cn/'), gevent.spawn(f, 'https://sohu.com/'), ]) 结果: GET: https://www.baidu.com/ GET: https://www.sina.com.cn/ GET: https://sohu.com/ 537854 bytes received from https://www.sina.com.cn/. 2443 bytes received from https://www.baidu.com/. 177385 bytes received from https://sohu.com/.
结论:当遇到IO的时候,就切换执行权了,去执行其他任务,然后等网络IO写回来再继续往下执行 import gevent import requests def f(url): print('GET: %s' % url) resp = requests.get(url) # 网络IO会切换执行权 data = resp.text print('%d bytes received from %s.' % (len(data), url)) gevent.joinall([ gevent.spawn(f, 'https://www.baidu.com/'), gevent.spawn(f, 'https://www.sina.com.cn/'), gevent.spawn(f, 'https://sohu.com/'), ]) 结果: GET: https://www.baidu.com/ 2443 bytes received from https://www.baidu.com/. GET: https://www.sina.com.cn/ 537846 bytes received from https://www.sina.com.cn/. GET: https://sohu.com/ 177385 bytes received from https://sohu.com/. 结论:如果没有monkey.patch_all(),那么协程会一个一个任务的执行,当遇到IO的时候,它也会等待内容返回,再去执行下一个任务
3)使用asyncio创建协程
import time import asyncio ''' 单协程实例 ''' now = time.time() # 返回当前时间戳 async def do_some_work(x): # 定义协程任务,前缀必须要写async print('Waiting: ', x) time.sleep(x) if __name__ == "__main__": start = time.time() coroutine1 = do_some_work(2) # 把协程任务的对象存放到变量里 coroutine2 = do_some_work(3) loop = asyncio.get_event_loop() # 创建一个事件循环 loop.run_until_complete(coroutine1) # 把协程注册到事件循环,并启动事件循环 loop.run_until_complete(coroutine2) # 把协程注册到事件循环,并启动事件循环 print('TIME: ', time.time() - start) 结果: Waiting: 2 Waiting: 3 TIME: 5.001837491989136
import time import asyncio ''' 绑定回调 ''' async def do_some_work(x): # 协程任务,前缀async print('Waiting: ', x) return 'Done after {}s'.format(x) def callback(a): # a 代表协程任务的对象 print('Callback: ', a.result()) # a.result() 固定写法,获取协程任务的返回值。 if __name__ == "__main__": start = time.time() coroutine = do_some_work(2) # 把协程任务的对象存放到变量里 loop = asyncio.get_event_loop() # 把协程注册到事件循环,并启动事件循环 task = asyncio.ensure_future(coroutine) # 创建一个task task.add_done_callback(callback) # 绑定回调 loop.run_until_complete(task) # 把test注册到事件循环,并启动事件循环 print('TIME: ', time.time() - start)
结果:
Waiting: 2
Callback: Done after 2s
TIME: 0.0009970664978027344