Python基礎之使用期物處理併發

Hanwencheng發表於2019-02-16

導語:本文章記錄了本人在學習Python基礎之控制流程篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習並交流。

本文重點:

1、掌握非同步程式設計的相關概念;
2、瞭解期物future的概念、意義和使用方法;
3、瞭解Python中的阻塞型I/O函式釋放GIL的特點。

一、非同步程式設計相關概念

阻塞:程式未得到所需計算資源時被掛起的狀態。換句話說,程式在等待某個操作完成期間,自身無法繼續幹別的事情,則稱該程式在該操作上是阻塞的。
併發:描述的是程式的組織結構。指程式要被設計成多個可獨立執行的子任務。併發以利用有限的計算機資源使多個任務可以被實時或近實時執行為目的。
並行:指的是多工同時執行的程式狀態,以利用多核CPU加速完成多工為目的。
非同步:為完成某個任務,不同程式單元之間過程中無需通訊協調,也能完成任務的方式。
不相關的程式單元之間可以是非同步的。簡言之,非同步意味著無序。
非同步程式設計:以程式、執行緒、協程、函式/方法作為執行任務的基本單位,結合回撥,事件迴圈、訊號量等機制,以提高整體執行效率和併發能力的程式設計方式。

二、期物

就下載國旗為目標實現的三個客戶端中,兩個HTTP併發客戶端比依序下載的指令碼效能高很多。
由此說明使用併發可以高效處理網路I/O。

期物(future)指一種物件,表示非同步執行的操作。
期物物件:concurrent.futures.Future或asyncio.Future類的例項。
三大方法:

  • Executor.submit():建立期物。
  • concurrent.futures.as_completed():迭代執行結束的期物,返回一個迭代器。
  • Executor.map(): 處理引數不同的同一個可呼叫物件。

小結:Executor.submit()加futures.as_completed()的組合比Executor.map()更靈活,因為submit()能處理不同的可呼叫物件和引數。

concurrent.futures模組的主要特色是ThreadPoolExecutor和ProcessPoolExecutor類,這兩個類實現的介面能分別在不同的執行緒或程式中執行可呼叫的物件。
注意:通常情況下自己不應該建立期物,而只能由併發框架(concurrent.futures或asyncio)例項化。

例項:concurrent.futures模組應用

from concurrent import futures

from flags import save_flag, get_flag, show, main 

MAX_WORKERS = 20

def download_one(cc): 
    image = get_flag(cc)
    show(cc)
    save_flag(image, cc.lower() + `.gif`)
    return cc

def download_many(cc_list):
    workers = min(MAX_WORKERS, len(cc_list))  
    with futures.ThreadPoolExecutor(workers) as executor: 
        res = executor.map(download_one, sorted(cc_list))  

    return len(list(res))

if __name__ == `__main__`:
    main(download_many)  

三、阻塞性I/O與GIL

Python標準庫中所有阻塞型I/O函式都會釋放全域性直譯器鎖(GIL),允許其他執行緒執行。
因此儘管有GIL,Python執行緒仍然適合在I/O密集型系統使用。

四、執行緒和多程式的替代方案

  • 對CPU密集型工作來說,要啟動多個程式,規避GIL。
  • 建立多程式最簡單的方式是使用futures.ProcessPoolExecutor類。
  • threading和multiprocessing模組:是Python中多執行緒和多程式併發的低層實現。

相關文章