Python并發(fā)concurrent.futures和asyncio實例
說明
Python標(biāo)準(zhǔn)庫為我們提供了threading和multiprocessing模塊編寫相應(yīng)的多線程/多進(jìn)程代碼。
從Python3.2開始,標(biāo)準(zhǔn)庫為我們提供了concurrent.futures模塊,concurrent.futures 模塊的主要特色是 ThreadPoolExecutor 和
ProcessPoolExecutor 類,這兩個類實現(xiàn)的接口能分別在不同的線程或進(jìn)程中執(zhí)行可調(diào)
用的對象。這兩個類在內(nèi)部維護(hù)著一個工作線程或進(jìn)程池,以及要執(zhí)行的任務(wù)隊列。
Python 3.4 以后標(biāo)準(zhǔn)庫中asyncio 包,這個包使用事件循環(huán)驅(qū)動的協(xié)程實現(xiàn)并發(fā)。這是 Python 中最大也
是最具雄心壯志的庫之一。asyncio 大量使用 yield from 表達(dá)式,因此與
Python 舊版不兼容。
submit和map方法
submit方法作用是向線程池提交可回調(diào)的task,并返回一個回調(diào)實例。
example:
import time from concurrent.futures import ThreadPoolExecutor # 可回調(diào)的task def pub_task(msg): time.sleep(3) return msg # 創(chuàng)建一個線程池 pool = ThreadPoolExecutor(max_workers=3) # 往線程池加入2個task task1 = pool.submit(pub_task, 'a') task2 = pool.submit(pub_task, 'b') print(task1.done()) # False time.sleep(4) print(task2.done()) # True print(task1.result()) print(task2.result())
map方法是創(chuàng)建一個迭代器,回調(diào)的結(jié)果有序放在迭代器中。
問題:
Executor.map 函數(shù)易于使用,不過有個特性可能有用,也可能沒用,具體情況取決于需求:這個函數(shù)返回結(jié)果的順序與調(diào)用開始的順序一致。
如果第一個調(diào)用生成結(jié)果用時 10秒,而其他調(diào)用只用 1 秒,代碼會阻塞 10 秒,獲取 map 方法返回的生成器產(chǎn)出的第一個結(jié)果。
在此之后,獲取后續(xù)結(jié)果時不會阻塞,因為后續(xù)的調(diào)用已經(jīng)結(jié)束。
如果必須等到獲取所有結(jié)果后再處理,這種行為沒問題;不過,通常更可取的方式是,不管提交的順序,只要有結(jié)果就獲取。
為此,要把 Executor.submit 方法和 futures.as_completed 函數(shù)結(jié)合起來使用。
from concurrent.futures import ThreadPoolExecutor
import requests
URLS = ['http://www.csdn.com', 'http://qq.com', 'http://www.leasonlove.cn']
def task(url, timeout=10):
return requests.get(url, timeout=timeout)
pool = ThreadPoolExecutor(max_workers=3)
results = pool.map(task, URLS)
for ret in results:
print('%s, %s' % (ret.url, ret))
future異步編程
Future可以理解為一個在未來完成的操作,這是異步編程的基礎(chǔ)。通常情況下,我們執(zhí)行io操作,訪問url時(如下)在等待結(jié)果返回之前會產(chǎn)生阻塞,cpu不能做其他事情,而Future的引入幫助我們在等待的這段時間可以完成其他的操作。
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
import requests
URLS = ['http://www.csdn.cn', 'http://qq.com', 'http://www.leasonlove.cn']
def task(url, timeout=1):
return requests.get(url, timeout=timeout)
with ThreadPoolExecutor(max_workers=3) as executor:
future_tasks = [executor.submit(task, url) for url in URLS]
for f in future_tasks:
if f.running():
print('%s is running' % str(f))
for f in as_completed(future_tasks):
try:
ret = f.done()
if ret:
f_ret = f.result()
print('%s, done, result: %s, %s' % (str(f), f_ret.url, f_ret.content))
except Exception as e:
# 第一個url無響應(yīng)
f.cancel()
print(str(e))
asyncio庫協(xié)程實現(xiàn)并發(fā)
對于gevent 和 asyncio 建議大家放棄Gevent,擁抱asyncio,asyncio是Python3.4以后標(biāo)準(zhǔn)庫。
而且由于Gevent直接修改標(biāo)準(zhǔn)庫里面大部分的阻塞式系統(tǒng)調(diào)用,包括socket、ssl、threading和 select等模塊,而變?yōu)閰f(xié)作式運行。
但是我們無法保證你在復(fù)雜的生產(chǎn)環(huán)境中有哪些地方使用這些標(biāo)準(zhǔn)庫會由于打了補(bǔ)丁而出現(xiàn)奇怪的問題。
import asyncio
import time
start = time.time()
async def do(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Finish after {}s'.format(x)
task1 = do(1)
task2 = do(2)
task3 = do(4)
tasks = [
asyncio.ensure_future(task1),
asyncio.ensure_future(task2),
asyncio.ensure_future(task3)
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print('Task result: ', task.result())
end = time.time()
print('TIME: ', end - start)
協(xié)程與線程
如果使用線程做過重要的編程,你就知道寫出程序有多么困難,因為調(diào)度程序任何時候都能中斷線程。
必須記住保留鎖,去保護(hù)程序中的重要部分,防止多步操作在執(zhí)行的過程中中斷,防止數(shù)據(jù)處于無效狀態(tài)。
而協(xié)程默認(rèn)會做好全方位保護(hù),以防止中斷。我們必須顯式產(chǎn)出才能讓程序的余下部分運行。
對協(xié)程來說,無需保留鎖,在多個線程之間同步操作,協(xié)程自身就會同步,因為在任意時刻只有一個協(xié)程運行。
想交出控制權(quán)時,可以使用 yield 或 yield from 把控制權(quán)交還調(diào)度程序。
這就是能夠安全地取消協(xié)程的原因:按照定義,協(xié)程只能在暫停的 yield處取消,因此可以處理 CancelledError 異常,執(zhí)行清理操作。
補(bǔ)充知識:Python-什么時候使用yield?
簡介
很多時候在python代碼中見到了yield,沒有系統(tǒng)學(xué)習(xí)過,自己也沒有用過。
yield語句延遲了語句的執(zhí)行,然后發(fā)送了一個值給調(diào)用者,但保留了一定的狀態(tài)去保證函數(shù)離開之后可以繼續(xù)。當(dāng)繼續(xù)的時候,函數(shù)繼續(xù)執(zhí)行上一個的運行狀態(tài)。這使得它的代碼可以隨著時間產(chǎn)生一系列的值,而不是立即執(zhí)行,然后像一個list一樣發(fā)送他們回來。
例子
例子1:
# A Simple Python program to demonstrate working # of yield # A generator function that yields 1 for first time, # 2 second time and 3 third time def simpleGeneratorFun(): yield 1 yield 2 yield 3 # Driver code to check above generator function for value in simpleGeneratorFun(): print(value)
返回語句發(fā)送一個特殊的值給它的調(diào)用者,而yield產(chǎn)生了一系列的值,當(dāng)我們想要遍歷一個序列的時候,我們應(yīng)該使用yield,但不想要把整個序列存儲在內(nèi)存中。
yield用于python的生成器(generator)。一個genertator 被定義得看起來像一個普通函數(shù)一樣,但它需要產(chǎn)生一個數(shù)字得時候,它使用yield,而不是使用return。如果一個函數(shù)里面定義了yield,那么它自動稱為了一個generator函數(shù)。、
例子2:
# A Python program to generate squares from 1
# to 100 using yield and therefore generator
# An infinite generator function that prints
# next square number. It starts with 1
def nextSquare():
i = 1;
# An Infinite loop to generate squares
while True:
yield i*i
i += 1 # Next execution resumes
# from this point
# Driver code to test above generator
# function
for num in nextSquare():
if num > 100:
break
print(num)
輸出1,4,9…100
以上這篇Python并發(fā)concurrent.futures和asyncio實例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
python生成可執(zhí)行exe控制Microsip自動填寫號碼并撥打功能
這篇文章主要介紹了python生成可執(zhí)行exe控制Microsip自動填寫號碼并撥打,在這需要注意一個問題,必須是已經(jīng)運行Microsip.exe文件,具體實現(xiàn)代碼跟隨小編一起看看吧2021-06-06
Pygame與OpenCV聯(lián)合播放視頻并保證音畫同步
Pygame的Movie模塊已經(jīng)廢棄多年,本文主要介紹了Pygame與OpenCV聯(lián)合播放視頻并保證音畫同步,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12
Python中torch.load()加載模型以及其map_location參數(shù)詳解
torch.load()作用用來加載torch.save()保存的模型文件,下面這篇文章主要給大家介紹了關(guān)于Python中torch.load()加載模型以及其map_location參數(shù)的相關(guān)資料,需要的朋友可以參考下2022-09-09
Python實現(xiàn)將VOC格式數(shù)據(jù)集轉(zhuǎn)換為YOLO格式數(shù)據(jù)集
本文將介紹如何將目標(biāo)檢測中常用的VOC格式數(shù)據(jù)集轉(zhuǎn)換為YOLO數(shù)據(jù)集,并進(jìn)行數(shù)據(jù)集比例劃分,從而方便的進(jìn)行YOLO目標(biāo)檢測,感興趣的小伙伴跟著小編一起來看看吧2025-06-06
python 在服務(wù)器上調(diào)用數(shù)據(jù)庫特別慢的解決過程
這篇文章主要介紹了python 在服務(wù)器上調(diào)用數(shù)據(jù)庫特別慢的解決過程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04

