Python協程(上)

幾個概念:

  • event_loop 事件循環:程序開啓一個無限的循環,程序員會把一些函數註冊到事件循環上。當知足事件發生的時候,調用相應的協程函數。
  • coroutine 協程:協程對象,指一個使用async關鍵字定義的函數,它的調用不會當即執行函數,而是會返回一個協程對象。協程對象須要註冊到事件循環,由事件循環調用。
  • task 任務:一個協程對象就是一個原生能夠掛起的函數,任務則是對協程進一步封裝,其中包含任務的各類狀態。
  • future: 表明未來執行或沒有執行的任務的結果。它和task上沒有本質的區別
  • async/await 關鍵字:python3.5 用於定義協程的關鍵字,async定義一個協程,await用於掛起阻塞的異步調用接口。

定義協程

經過async關鍵字定義一個協程(coroutine),協程也是一種對象。協程不能直接運行,須要把協程加入到事件循環(loop),由後者在適當的時候調用協程。asyncio.get_event_loop方法能夠建立一個事件循環,而後使用run_until_complete將協程註冊到事件循環,並啓動事件循環。python

import time
import asyncio

async def task(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)

start = time.time()

coroutine = task(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine)

print('用時:', time.time()-start)

建立task

協程對象不能直接運行,在註冊事件循環的時候,實際上是run_until_complete方法將協程包裝成爲了一個任務(task)對象。所謂task對象是Future類的子類。保存了協程運行後的狀態,用於將來獲取協程的結果。程序員

import asyncio
import time

async def task(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)


start = time.time()

coroutine = task(2)
loop = asyncio.get_event_loop()
task = loop.create_task(coroutine)      # task = asyncio.ensure_future(coroutine)
print(task)
loop.run_until_complete(task)
print(task)
print('用時:', time.time() - start)

建立task後,task在加入事件循環以前是pending狀態,執行以後是finished狀態。網絡

asyncio.ensure_future(coroutine) 和loop.create_task(coroutine)
均可以建立一個task,run_until_complete的參數是一個futrue對象。當傳入一個協程,其內部會自動封裝成task,task是Future的子類。isinstance(task, asyncio.Future)將會輸出True。併發

綁定回調

綁定回調,在task執行完畢的時候能夠獲取執行的結果,回調的最後一個參數是future對象,經過該對象能夠獲取協程返回值。若是回調須要多個參數,能夠經過偏函數導入。異步

import time
import asyncio

async def task(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

def callback(future):
    print('Callback: ', future.result())


coroutine = task(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
loop.run_until_complete(task)

coroutine執行結束時候會調用回調函數。並經過參數future獲取協程執行的結果。咱們建立的task和回調裏的future對象,其實是同一個對象。async

future 與 result

回調中咱們使用了future對象的result方法。前面不綁定回調的例子中,咱們能夠看到task有fiinished狀態。在那個時候,能夠直接讀取task的result方法。函數

async def task(x):
    print('Waiting {}'.format(x))
    return 'Done after {}s'.format(x)

coroutine = task(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)

# task.result()是協程對象的返回值
print('Task result: {}'.format(task.result()))

阻塞和await

使用async能夠定義協程對象,使用await能夠針對耗時的操做進行掛起,就像生成器裏的yield同樣,函數讓出控制權。協程遇到await,事件循環將會掛起該協程,執行別的協程,直到其餘的協程也掛起或者執行完畢,再進行下一個協程的執行。oop

耗時的操做通常是一些IO操做,例如網絡請求,文件讀取等。咱們使用asyncio.sleep函數來模擬IO操做。協程的目的也是讓這些IO操做異步化。code

import asyncio
import time

async def task(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

start = time.time()

coroutine = task(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)

print('Task result: ', task.result())
print('Time: ', time.time() - start)

在 sleep的時候,使用await讓出控制權。即當遇到阻塞調用的函數的時候,使用await方法將協程的控制權讓出,以便loop調用其餘的協程。如今咱們的例子就用耗時的阻塞操做了。orm

併發和並行

asyncio實現併發,就須要多個協程來完成任務,每當有任務阻塞的時候就await,而後其餘協程繼續工做。建立多個協程的列表,而後將這些協程註冊到事件循環中。

import asyncio
import time

async def task(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

start = time.time()

coroutine1 = task(1)   # <coroutine object task at 0x000001E583341258>
coroutine2 = task(2)
coroutine3 = task(4)

loop = asyncio.get_event_loop()
tasks = [                             # 建立任務
    asyncio.ensure_future(coroutine1),
    asyncio.ensure_future(coroutine2),
    asyncio.ensure_future(coroutine3)
]
loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:
    print('Task result: ', task.result())
    
print('Time: ', time.time() - start)

# 當任務比較多的時候,可使用列表生成式,效果是同樣的。
import asyncio
import time

async def task(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

start = time.time()

loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(task(i)) for i in [1,2,4]]
loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:
    print('Task result: ', task.result())

print('Time: ', time.time() - start)

使用aysncio實現了併發。asyncio.wait(tasks) 也可使用 asyncio.gather(*tasks) ,前者接受一個task列表,後者接收一堆task。

相關文章
相關標籤/搜索