先來一個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()
