python 使用期物处理并发
文章目錄
- 1. futures.ThreadPoolExecutor
- 2. 期物
- 3. 阻塞型I/O和GIL
- 4. 使用concurrent.futures模塊啟動(dòng)進(jìn)程
learning from 《流暢的python》
1. futures.ThreadPoolExecutor
import os import time import sys import requestsPOP20_CC = ('CN IN US ID BR PK NG BD RU JP ' 'MX PH VN ET EG DE IR TR CD FR').split() BASE_URL = 'http://flupy.org/data/flags' DEST_DIR = './'def save_flag(img, filename): # 保存圖像path = os.path.join(DEST_DIR, filename)with open(path, 'wb') as fp:fp.write(img)def get_flag(cc): # 獲取圖像url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())resp = requests.get(url)return resp.contentdef show(text): # 打印信息print(text, end=' ')sys.stdout.flush()def download_many(cc_list):for cc in sorted(cc_list):image = get_flag(cc) # 獲取show(cc) # 打印save_flag(image, cc.lower() + '.gif') # 保存return len(cc_list)def main(download_many):t0 = time.time()count = download_many(POP20_CC)elapsed = time.time() - t0msg = '\n{} flags downloaded in {:.2f}s'print(msg.format(count, elapsed)) # 計(jì)時(shí)信息# ----使用 futures.ThreadPoolExecutor 類實(shí)現(xiàn)多線程下載 from concurrent import futuresMAX_WORKERS = 20 # 最多使用幾個(gè)線程def download_one(cc):image = get_flag(cc)show(cc)save_flag(image, cc.lower() + '.gif')return ccdef download_many_1(cc_list):workers = min(MAX_WORKERS, len(cc_list))with futures.ThreadPoolExecutor(workers) as executor:# 使用工作的線程數(shù)實(shí)例化 ThreadPoolExecutor 類;# executor.__exit__ 方法會(huì)調(diào)用 executor.shutdown(wait=True) 方法,# 它會(huì)在所有線程都執(zhí)行完畢 前阻塞線程res = executor.map(download_one, sorted(cc_list))# download_one 函數(shù) 會(huì)在多個(gè)線程中并發(fā)調(diào)用;# map 方法返回一個(gè)生成器,因此可以迭代, 獲取各個(gè)函數(shù)返回的值return len(list(res))if __name__ == '__main__':# main(download_many) # 24 秒main(download_many_1) # 3 秒2. 期物
- 通常不應(yīng)自己創(chuàng)建期物
- 只能由并發(fā)框架(concurrent.futures 或 asyncio)實(shí)例化
原因:期物 表示終將發(fā)生的事情,其 執(zhí)行的時(shí)間 已經(jīng)排定。因此,只有排定把某件事交給 concurrent.futures.Executor 子類處理時(shí),才會(huì)創(chuàng)建 concurrent.futures.Future 實(shí)例
例如,Executor.submit() 方法的參數(shù)是一個(gè)可調(diào)用的對象,調(diào)用這個(gè)方法后會(huì)為傳入的可調(diào)用對象 排期,并返回一個(gè)期物
3. 阻塞型I/O和GIL
CPython 解釋器本身就不是線程安全的,因此有全局解釋器鎖(GIL), 一次只允許使用一個(gè)線程執(zhí)行 Python 字節(jié)碼。因此,一個(gè) Python 進(jìn)程 通常不能同時(shí)使用多個(gè) CPU 核心
標(biāo)準(zhǔn)庫中所有執(zhí)行阻塞型 I/O 操作的函數(shù),在等待操作系統(tǒng)返回結(jié)果時(shí) 都會(huì)釋放 GIL。
這意味著在 Python 語言這個(gè)層次上可以使用多線程,而 I/O 密集型 Python 程序能從中受益:一個(gè) Python 線程等待網(wǎng)絡(luò)響應(yīng)時(shí),阻塞型 I/O 函數(shù)會(huì)釋放 GIL,再運(yùn)行一個(gè)線程(網(wǎng)絡(luò)下載,文件讀寫都屬于 IO 密集型)
4. 使用concurrent.futures模塊啟動(dòng)進(jìn)程
這個(gè)模塊實(shí)現(xiàn)的是真正 的并行計(jì)算,因?yàn)樗褂?ProcessPoolExecutor 類把工作分配給多個(gè) Python 進(jìn)程處理。
因此,如果需要做 CPU 密集型處理,使用這個(gè)模塊 能繞開 GIL,利用所有可用的 CPU 核心
點(diǎn)擊查看:進(jìn)程、線程概念差異
使用 concurrent.futures 模塊能特別輕松地 把 基于線程 的方案轉(zhuǎn)成 基于進(jìn)程 的方案
ProcessPoolExecutor 的價(jià)值體現(xiàn)在 CPU 密集型 作業(yè)上
總結(jié)
以上是生活随笔為你收集整理的python 使用期物处理并发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode 1991. 找到数组的
- 下一篇: websocket python爬虫_p