Python concurrent.futures 文檔翻譯

摘要:

本文主要是對 Python3 標準庫 concurrent.futures 文檔的翻譯


concurrent.futures 模塊爲異步執行可調用的對象提供了一個高級的接口。

異步執行可以通過線程來實現,使用 ThreadPoolExecutor 模塊,或者使用 ProcessPoolExecutor 模塊通過分離進程來實現。兩種實現都有同樣的接口,他們都是通過抽象類 Executor 來定義的。

Executor 對象

class concurrent.futures.Executor

這是一個抽象類,用來提供方法去支持異步地執行調用,它不應該被直接調用,而是應該通過具體的子類來使用。

submit(fn, *args, **kwargs)

可調用對象的調度器,fn參數將會以fn(*args, **kwargs)的形式來調用,同時返回一個 Future 對象代表了可調用對象的執行情況。

“`python
with ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(pow, 323, 1235)
print(future.result())


> `map(func, *iterables, timeout=None, chunksize=1)`

>> 和`map(func, *iterables)`函數的作用基本相同,除了`func`是被異步執行的,而且幾個對於`func`調用可能是同時執行的。這個函數返回的迭代器調用`__next__()`方法的時候,如果在`timeout`秒內結果不可用,那麼迭代器將會從原始調用的函數向`Executor.map()`拋出一個`concurrent.futures.TimeoutError`的異常。`timeout`既能是一個整數,也能是一個浮點數。如果`timeout`沒有指定的話或者等於 None 的話,那麼等待時間就沒有限制。如果調用函數拋出了一個異常,那麼當迭代器取到這個函數的時候,異常將會被拋出。
>> 當使用`ProcessPoolExecutor`的時候,這個方法將`iterables`切成許多塊,然後將這些內容作爲分離的任務提交到進程池中。每個塊的大概的尺寸能夠通過`chunksize`(大於0的正整數)的參數來指定。當`iterables`非常大的時候,和`chunksize`默認等於1相比,將`chunksize`設置爲一個很大的值,將會顯著地提升性能。在使用`ThreadPoolExecutor`的情況下,`chunksize`的大小沒有影響。

>> Python 3.5新增功能:添加了`chunksize`參數

> `shutdown(wait=True)`

>> 告訴執行器,噹噹前阻塞的 futures 執行完了以後,它應該釋放所有它使用的資源。在`shutdown`函數之後再來調用`Executor.submit()`和`Executor.map()`將會拋出`RuntimeError`

>> 如果`wait`等True 的話,這個方法不會立即返回,而直到所有阻塞的 futures 都返回,而且和這個執行器所有相關的資源都被釋放以後,這個函數纔會返回。 如果`wait`設置爲 False ,那麼這個方法會立刻返回,而和這個執行器所有相關的資源只有等到所有阻塞的 futures 都執行完以後纔會被釋放。而無論`wait`參數的值是什麼,整個 Python 程序都會等到所有阻塞的 futures 執行完畢以後纔會退出。

>> 通過`with`語句,可以避免明確地來調用這個方法,它在執行完以後將會自動關閉`Executor`。(調用 Executor.shutdown() 時`wait`會被設置爲True,這將會等待所有 future 執行完畢)

>> ```python
import shutil
with ThreadPoolExecutor(max_workers=4) as e:
    e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
    e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
    e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
    e.submit(shutil.copy, 'src4.txt', 'dest4.txt')

ThreadPoolExecutor

ThreadPoolExecutorExecutor的子類,使用一個線程池去異步地執行調用。

當一個 Future 關聯的調用等待另外一個 Future 的執行結果的時候,死鎖就有可能發生,例如下面的例子:

import time

def wait_on_b():
    time.sleep(5)
    print(b.result())  # b 永遠不會完成,因爲它等待着 a 的結果
    return 5

def wait_on_a():
    time.sleep(5)
    print(a.result()) # a 永遠不會完成,因爲它等待着 b 的結果
    return 6

executor = ThreadPoolExecutor(max_workers=2)
a = executor.submit(wait_on_b)
b = executor.submit(wait_on_a)

和這個例子:

def wait_on_future():
    f = executor.submit(pow, 5, 2)
    # 這個也永遠不會完成,因爲線程池裏面最多只能有一個線程,而它現在正在執行着這個函數。
    print(f.result())

executor = ThreadPoolExecutor(max_workers=1)
executor.submit(wait_on_future)

class concurrent.futures.ThreadPoolExecutor(max_workers=None)

一個Executor的子類,使用線程池中最多max_workers個線程去異步地執行回調。
Python 3.5中的改變:如果max_workers參數爲None或者沒有給定,那麼它將會被默認設置成爲機器的CPU核數乘5。這裏假設ThreadPoolExecutor經常被用來執行IO密集型的工作而不是CPU密集型的工作,工作者的個數應該比ProcessPoolExecutor的工作者的個數要多。

ThreadPoolExecutor 例子

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# 獲取一個單頁,同時報告URL和內容
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# 我們可以通過with語句來確保線程能夠被及時地清理
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

ProcessPoolExecutor

ProcessPoolExecutor 類是Executor的一個子類,它使用一個進程池來異步地執行調用。ProcessPoolExecutor使用multiprocessing模塊,它允許去避免全局解釋器鎖,但同時也意味着僅僅只有pickable(TODO譯者注:這裏這個單詞的含義我還沒理解)對象能夠被執行和返回。

__main__模塊必須能夠被作爲工作者的子模塊導入。這意味着ProcessPoolExecutor將不會在交互式的解釋器中工作。

從一個已添加到Executor中的可調用對象中調用Executor或者Future的方法將會導致死鎖(TODO譯者注:有待實踐操作)。

class concurrent.futures.ProcessPoolExecutor(max_workers=None)

一個Executor的子類來異步地執行調用,最多將會使用進程池中max_workers個工作進程。如果max_workersNone或者沒有給出的話。它默認將會使用機器CPU的個數來作爲最大進程數的值。如果max_workers小於或者等於0,那麼將會拋出一個ValueError
3.3版本中的改變:當一個工作進程被突然終止了後,將會拋出一個BrokenProcessPool的錯誤。以前的情況是,行爲是未定義的但是 Executor 上的操作或者他自己的 future 將會被凍結或者導致死鎖。

ProcessPoolExecutor 的例子

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    """(譯者注) 判斷素數的程序

    這裏對 sqrt_n 做一點解釋:

    假設 n 不是素數,那麼有 n = x * y( x != 1 and y != 1)
    因爲 n = x * y, 所以 x <= sqrt(n) or y <= sqrt(n)
    所以 i in [2, sqrt(n)]; i表示能夠被 n 整除的數
    所以如果 i not in [2, sqrt(n)]; 那麼n是素數

    在這個程序中我們在一開始就判斷過2,所以循環從3開始,且跳過所有偶數
    """
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()

Future 對象

Future 類封裝了一個可調用對象的異步執行過程,Future 對象是通過Executor.submit()函數來創建的。

class concurrent.futures.Future

封裝了一個可調用對象的異步執行過程。Future 實例是通過Executor.submit()方法來創建的,而且不應該被直接創建,除非用來測試。

cancel()

嘗試去取消相關回調,如果這個回調正在被執行,而且不能被取消,那麼這個方法將會返回False,否則這個方法將會取消相應的回調並且返回True

cancelled()

如果相關回調被成功取消了,那麼這個方法將會返回True

running()

如果相關回調當前正在被執行而且無法取消,那麼將會返回True

done()

如果相關的回調被成功地取消或者已經運行完畢那麼將返回True

result(timeout=None)

返回由相關回調產生的結果。如果這個回調還沒有被完成那麼這個方法將會等待timeout秒。如果這個回調在timeout秒內還沒有返回,一個concurrent.futures.TimeoutError的異常將會被拋出。timeout能夠被設置成一個整數或者一個浮點數。如果timeout沒有被設置或者其值爲None,那麼等待時間將沒有限制。

如果這個 future 在完成之前被取消了,那麼將會拋出一個CancelledError的異常。
如果相關的回調拋出了一個異常,那麼這個方法也會相應地拋出這個異常。

exception(timeout=None)

返回由相關回調拋出的異常。如果相關回調還沒有被完成那麼這個方法將會等待timeout秒。如果相關回調在timeout秒內還沒有被完成,那麼將會拋出一個concurrent.futures.TimeoutError的異常。timeout能夠被設置成一個整數或者一個浮點數。如果timeout沒有被設置或者其值爲None,那麼等待時間將沒有限制。

如果這個 future 在完成之前被取消了,那麼將會拋出一個CancelledError的異常。
如果相關回調被完成了且沒有拋出異常,None將會被返回。

add_done_callback(fn)

將可調用對象fn連接到這個 future 上,fn將會在 future 被取消或者結束運行時被調用,而且僅有相關 future 這一個參數。
添加的可調用對象將會以它們被添加的順序來調用,而且總是在添加它們的那個進程的所屬的線程中調用(譯者注,可以參考這段代碼)。如果相關調用fn拋出了一個Exception子類的異常,它將會被記錄和忽略。如果相關調用fn拋出了一個BaseException子類的異常,那麼行爲是未定義的。
如果相關的 future 已經被完成了或者取消了,fn將會被立刻調用。

如下的Future方法意味着可以在單元測試或者Exectuor的實現中使用。

set_running_or_notify_cancel()

這個方法應該僅能夠被Exectuor的實現(在執行和Future相關的工作之前調用)和單元測試調用。
如果這個方法返回False,那麼相關的Future被取消了。即Future.cancel()被調用了而且返回True。任何等待 Future 完成的線程(即通過as_completed或者wait()方法等待)都會被喚醒。
這個方法僅能被調用一次,而且不能在Future.set_result()或者Future.set_exception()被調用了之後調用。

set_result(result)

將 future 相關的工作的結果設置成result
這個方法僅僅應該被Exectuor實現的時候和單元測試來調用。

set_exception(exception)

將 future 相關的工作的結果設置成異常exception
這個方法僅僅應該被Exectuor實現的時候和單元測試來調用。

模塊方法

concurrent.futures.wait(fs, timeout=None, return_when=ALL_COMPLETED)

等待由fs(譯者注:這裏這個fs表示 futures 的意思)給出的 future 實例(可能由不同的 Exectuor 實例創建的)去完成。返回一個命名二元組的集合。在第一個集合元素中,命名爲done,包含着那些在wait函數完成之前就已經完成(被完成或者被取消)的 future。第二個集合元素命名爲not_done,包含那些未完成的 future。
timeout用來控制wait函數在返回之前等待的最大秒數。timeout能夠被設置成整數或者浮點數。如果timeout沒有被設置或者值爲None,那麼等待時間將沒有限制。
return_when代表了函數應該在什麼時候返回,它必須被設置成如下值中的一個:

Constant
Description
FIRST_COMPLETED 當任何 future 結束或者被取消的時候,這個函數就會返回
FIRST_EXCEPTION 當任何 future 通過拋出異常來結束的時候,這個函數就會返回,如果沒有任何函數拋出異常,那麼它等價與ALL_COMPLETED
ALL_COMPLETED 當所有的 future 都已結束或者被取消的時候,這個函數就會返回。

concurrent.futures.as_completed(fs, timeout=None)

返回一個在由fs給出的 future 實例(可能由不同的Executor創建的)上的迭代器,當 future 完成的時候(結束或者被取消),就把這個 future yield回去。如果fs中給出的 future 重複了,那麼將僅會被返回一次。任何在as_completed函數調用之前就已經完成或者被取消的 future, 將會首先yield回來。如果__next__()被調用,在timeout秒後結果依然不可達,那麼返回的迭代器將會從原始的調用向as_completed拋出一個concurrent.futures.TimeoutError異常。timeout可以被設置成整數或者浮點數。如果timeout沒有被指定或者其值爲None,那麼等待的時間將沒有限制。

See also:
PEP 3148 - futures - 執行異步計算
這個提案描述了包含在 Python 標準庫中的這個特性。

異常類

exception concurrent.futures.CancelledError

當 future 被取消的時候拋出這個異常。

exception concurrent.futures.TimeoutError

當 future 的操作超出給定的timeout時間的時候拋出這個異常。

exception concurrent.futures.process.BrokenProcessPool

派生自RuntimeError,當ProcessPoolExecutor中的一個工作進程以不乾淨的方式(例如,它是從外部被殺死的)結束的時候,這個異常將會拋出。
Python 3.3 後的新特性

發佈了69 篇原創文章 · 獲贊 17 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章