Python 協程總結

Python 協程總結

理解

協程,又稱爲微線程,看上去像是子程序,可是它和子程序又不太同樣,它在執行的過程當中,能夠在中斷當前的子程序後去執行別的子程序,再返回來執行以前的子程序,可是它的相關信息仍是以前的。html

優勢:python

  1. 極高的執行效率,由於子程序切換而不是線程切換,沒有了線程切換的開銷;
  2. 不須要多線程的鎖機制,由於只有一個線程在執行;

若是要充分利用CPU多核,能夠經過使用多進程+協程的方式web

使用

打開asyncio的源代碼,能夠發現asyncio中的須要用到的文件以下:數組

下面的則是接下來要總結的文件多線程

文件 解釋
base_events 基礎的事件,提供了BaseEventLoop事件
coroutines 提供了封裝成協程的類
events 提供了事件的抽象類,好比BaseEventLoop繼承了AbstractEventLoop
futures 提供了Future類
tasks 提供了Task類和相關的方法

coroutines

函數 解釋
coroutine(func) 爲函數加上裝飾器
iscoroutinefunction(func) 判斷函數是否使用了裝飾器
iscoroutine(obj) 判斷該對象是不是裝飾器

若是在函數使用了coroutine裝飾器,就能夠經過yield from去調用async def聲明的函數,若是已經使用async def聲明,就沒有必要再使用裝飾器了,這兩個功能是同樣的。app

import asyncio
@asyncio.coroutine
def hello_world():
    print("Hello World!")
async def hello_world2():
    print("Hello World2!")
print('------hello_world------')
print(asyncio.iscoroutinefunction(hello_world))
print('------hello_world2------')
print(asyncio.iscoroutinefunction(hello_world2))
print('------event loop------')
loop = asyncio.get_event_loop()
# 一直阻塞該函數調用到函數返回
loop.run_until_complete(hello_world())
loop.run_until_complete(hello_world2())
loop.close()

上面的代碼分別使用到了coroutine裝飾器和async def,其運行結果以下:異步

------hello_world------True------hello_world2------True------event loop------Hello World!Hello World2!async

注意:不能夠直接調用協程,須要一個event loop去調用。ide

若是想要在一個函數中去獲得另一個函數的結果,能夠使用yield from或者await,例子以下:函數

import asyncio
async def compute(x, y):
    print("Compute %s + %s ..." % (x, y))
    await asyncio.sleep(1.0)
    return x + y
async def print_sum(x, y):
    result = await compute(x, y)
    print("%s + %s = %s" % (x, y, result))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()

函數print_sum會一直等到函數compute返回結果,執行過程以下:

base_events

這個文件裏面漏出來的只有BaseEventLoop一個類,它的相關方法以下:

函數 解釋
create_future() 建立一個future對象而且綁定到事件上
create_task() 建立一個任務
run_forever() 除非調用stop,不然事件會一直運行下去
run_until_complete(future) 直到future對象執行完畢,事件才中止
stop() 中止事件
close() 關閉事件
is_closed() 判斷事件是否關閉
time() 返回事件運行時的時間
call_later(delay, callback, *args) 設置一個回調函數,而且能夠設置延遲的時間
call_at(when, callback, *args) 同上,可是設置的是絕對時間
call_soon(callback, *args) 立刻調用

events

函數 解釋
get_event_loop() 返回一個異步的事件
... ...

返回的就是BaseEventLoop的對象。

future

Future類的相關方法以下:

方法 解釋
cancel() 取消掉future對象
cancelled() 返回是否已經取消掉
done() 若是future已經完成則返回true
result() 返回future執行的結果
exception() 返回在future中設置了的exception
add_done_callback(fn) 當future執行時執行回調函數
remove_done_callback(fn) 刪除future的全部回調函數
set_result(result) 設置future的結果
set_exception(exception) 設置future的異常

設置future的例子以下:

import asyncio
async def slow_operation(future):
    await asyncio.sleep(1)  # 睡眠
    future.set_result('Future is done!')  # future設置結果
loop = asyncio.get_event_loop()
future = asyncio.Future()  # 建立future對象
asyncio.ensure_future(slow_operation(future))  # 建立任務
loop.run_until_complete(future)  # 阻塞直到future執行完才中止事件
print(future.result())
loop.close()

run_until_complete方法在內部經過調用了future的add_done_callback,當執行future完畢的時候,就會通知事件。

下面這個例子則是經過使用future的add_done_callback方法實現和上面例子同樣的效果:

import asyncio
async def slow_operation(future):
    await asyncio.sleep(1)
    future.set_result('Future is done!')
def got_result(future):
    print(future.result())
    loop.stop()  # 關閉事件
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(slow_operation(future))
future.add_done_callback(got_result)  # future執行完畢就執行該回調
try:
    loop.run_forever()
finally:
    loop.close()

一旦slow_operation函數執行完畢的時候,就會去執行got_result函數,裏面則調用了關閉事件,因此不用擔憂事件會一直執行。

task

Task類是Future的一個子類,也就是Future中的方法,task均可以使用,類方法以下:

方法 解釋
current_task(loop=None) 返回指定事件中的任務,若是沒有指定,則默認當前事件
all_tasks(loop=None) 返回指定事件中的全部任務
cancel() 取消任務

並行執行三個任務的例子:

import asyncio
async def factorial(name, number):
    f = 1
    for i in range(2, number+1):
        print("Task %s: Compute factorial(%s)..." % (name, i))
        await asyncio.sleep(1)
        f *= i
    print("Task %s: factorial(%s) = %s" % (name, number, f))
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
    factorial("A", 2),
    factorial("B", 3),
    factorial("C", 4),
))
loop.close()

執行結果爲

Task A: Compute factorial(2)...Task B: Compute factorial(2)...Task C: Compute factorial(2)...Task A: factorial(2) = 2Task B: Compute factorial(3)...Task C: Compute factorial(3)...Task B: factorial(3) = 6Task C: Compute factorial(4)...Task C: factorial(4) = 24

能夠發現,ABC同時執行,直到future執行完畢才退出。

下面一些方法是和task相關的方法

方法 解釋
as_completed(fs, *, loop=None, timeout=None) 返回是協程的迭代器
ensure_future(coro_or_future, *, loop=None) 調度執行一個 coroutine object:而且它封裝成future。返回任務對象
async(coro_or_future, *, loop=None) 丟棄的方法,推薦使用ensure_future
wrap_future(future, *, loop=None) Wrap a concurrent.futures.Future object in a Future object.
gather(*coros_or_futures, loop=None, return_exceptions=False) 從給定的協程或者future對象數組中返回future彙總的結果
sleep(delay, result=None, *, loop=None) 建立一個在給定時間(以秒爲單位)後完成的協程
shield(arg, *, loop=None) 等待future,屏蔽future被取消
wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED) 等待由序列futures給出的Futures和協程對象完成。協程將被包裹在任務中。返回含兩個集合的Future:(done,pending)
wait_for(fut, timeout, *, loop=None) 等待單個Future或coroutine object完成超時。若是超時爲None,則阻止直到future完成

參考文章

官方文檔

相關文章
相關標籤/搜索