先来一个asyncio程序
import asyncio,random @asyncio.coroutine def smart_fib(n): index = 0 a = 0 b = 1 while index < n: sleep_secs = random.uniform(0, 0.2) yield from asyncio.sleep(sleep_secs) #通常yield from后都是接的耗时操作 print('Smart one think {} secs to get {}'.format(sleep_secs, b)) a, b = b, a + b index += 1 @asyncio.coroutine def stupid_fib(n): index = 0 a = 0 b = 1 while index < n: sleep_secs = random.uniform(0, 0.4) yield from asyncio.sleep(sleep_secs) #通常yield from后都是接的耗时操作 print('Stupid one think {} secs to get {}'.format(sleep_secs, b)) a, b = b, a + b index += 1 if __name__ == '__main__': loop = asyncio.get_event_loop() tasks = [ smart_fib(10), stupid_fib(10), ] loop.run_until_complete(asyncio.wait(tasks)) print('All fib finished.') loop.close()
asyncio.wait()监听一个协程任务列表
yield from后面接的asyncio.sleep()是一个coroutine(里面也用了yield from),所以线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。当asyncio.sleep()返回时,线程就可以从yield from拿到返回值(此处是None),然后接着执行下一行语句。
asyncio是一个基于事件循环的实现异步I/O的模块。通过yield from,我们可以将协程asyncio.sleep的控制权交给事件循环,然后挂起当前协程;之后,由事件循环决定何时唤醒asyncio.sleep,接着向后执行代码。
协程之间的调度都是由事件循环决定。 yield from asyncio.sleep(sleep_secs)
这里不能用time.sleep(1)
因为time.sleep()返回的是None,它不是iterable,还记得前面说的yield from后面必须跟iterable对象(可以是生成器,迭代器)。
async和await
弄清楚了asyncio.coroutine和yield from之后,在Python3.5中引入的async和await就不难理解了:可以将他们理解成asyncio.coroutine/yield from的完美替身。当然,从Python设计的角度来说,async/await让协程表面上独立于生成器而存在,将细节都隐藏于asyncio模块之下,语法更清晰明了。
加入新的关键字 async ,可以将任何一个普通函数变成协程
import time,asyncio,random async def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) print(alist.pop(c)) a = ["aa","bb","cc"] c=mygen(a) print(c) 输出: <coroutine object mygen at 0x02C6BED0>
在上面程序中,我们在前面加上async,该函数就变成一个协程了。
但是async对生成器是无效的。async无法将一个生成器转换成协程。
还是刚才那段代码,我们把print改成yield
async def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) yield alist.pop(c) a = ["ss","dd","gg"] c=mygen(a) print(c) 可以看到输出 <async_generator object mygen at 0x02AA7170>
并不是coroutine 协程对象
所以我们的协程代码应该是这样的
import time,asyncio,random async def mygen(alist): while len(alist) > 0: c = random.randint(0, len(alist)-1) print(alist.pop(c)) await asyncio.sleep(1) strlist = ["ss","dd","gg"] intlist=[1,2,5,6] c1=mygen(strlist) c2=mygen(intlist) print(c1)
要运行协程,要用事件循环
在上面的代码下面加上:
if __name__ == '__main__': loop = asyncio.get_event_loop() tasks = [ c1, c2 ] loop.run_until_complete(asyncio.wait(tasks)) print('All fib finished.') loop.close()