python协成_Python协程(上)
幾個概念:
event_loop 事件循環:程序開啟一個無限的循環,程序員會把一些函數注冊到事件循環上。當滿足事件發生的時候,調用相應的協程函數。
coroutine 協程:協程對象,指一個使用async關鍵字定義的函數,它的調用不會立即執行函數,而是會返回一個協程對象。協程對象需要注冊到事件循環,由事件循環調用。
task 任務:一個協程對象就是一個原生可以掛起的函數,任務則是對協程進一步封裝,其中包含任務的各種狀態。
future: 代表將來執行或沒有執行的任務的結果。它和task上沒有本質的區別
async/await 關鍵字:python3.5 用于定義協程的關鍵字,async定義一個協程,await用于掛起阻塞的異步調用接口。
定義協程
通過async關鍵字定義一個協程(coroutine),協程也是一種對象。協程不能直接運行,需要把協程加入到事件循環(loop),由后者在適當的時候調用協程。asyncio.get_event_loop方法可以創建一個事件循環,然后使用run_until_complete將協程注冊到事件循環,并啟動事件循環。
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對象,實際上是同一個對象。
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,事件循環將會掛起該協程,執行別的協程,直到其他的協程也掛起或者執行完畢,再進行下一個協程的執行。
耗時的操作一般是一些IO操作,例如網絡請求,文件讀取等。我們使用asyncio.sleep函數來模擬IO操作。協程的目的也是讓這些IO操作異步化。
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調用其他的協程。現在我們的例子就用耗時的阻塞操作了。
并發和并行
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) #
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。
總結
以上是生活随笔為你收集整理的python协成_Python协程(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 切割 字符串_web前端如何使用字符串
- 下一篇: c primer英文版第5版_2019足