Python中的非同步程式設計 Asyncio

2021-09-23 08:58:15 字數 4853 閱讀 1983

如果你已經決定要理解 python 的非同步部分,歡迎來到我們的「asyncio how-to 」。

注:哪怕連異動正規化的存在都不知道的情況下,你也可以成功地使用 python。但是,如果你對底層執行模式感興趣的話,asyncio 絕對值得檢視。

非同步是怎麼一回事?

在傳統的順序程式設計中, 所有傳送給直譯器的指令會一條條被執行。此類**的輸出容易顯現和**。 但是…

譬如說你有乙個指令碼向3個不同伺服器請求資料。 有時,誰知什麼原因,傳送給其中乙個伺服器的請求可能意外地執行了很長時間。想象一下從第二個伺服器獲取資料用了10秒鐘。在你等待的時候,整個指令碼實際上什麼也沒乾。如果你可以寫乙個指令碼可以不去等待第二個請求而是僅僅跳過它,然後開始執行第三個請求,然後回到第二個請求,執行之前離開的位置會怎麼樣呢。就是這樣。你通過切換任務最小化了空轉時間。儘管如此,當你需要乙個幾乎沒有i/o的簡單指令碼時,你不想用非同步**。

還有一件重要的事情要提,所有**在乙個執行緒中執行。所以如果你想讓程式的一部分在後台執行同時幹一些其他事情,那是不可能的。

準備開始

這是 asyncio 主概念最基本的定義:

class task(futures.future):   

def __init__(self, coro, loop=none): 

super().__init__(loop=loop) 

... 

self._loop.call_soon(self._step) 

def _step(self): 

... 

try: 

... 

result = next(self._coro) 

except stopiteration as exc: 

self.set_result(exc.value) 

except baseexception as exc: 

self.set_exception(exc) 

raise 

else: 

... 

self._loop.call_soon(self._step)  

現在我們看一下所有這些如何融為一體。正如我之前提到的,非同步**在乙個執行緒中執行。

從上圖可知:

2.從佇列中取得任務

3.每個任務在協程中執行下一步動作

4.如果在乙個協程中呼叫另乙個協程(await ),會觸發上下文切換,掛起當前協程,並儲存現場環境(變數,狀態),然後載入被呼叫協程

5.如果協程的執行到阻塞部分(阻塞i/o,sleep),當前協程會掛起,並將控制權返回到執行緒的訊息迴圈中,然後訊息迴圈繼續從佇列中執行下乙個任務...以此類推

6.佇列中的所有任務執行完畢後,訊息迴圈返回第乙個任務

非同步和同步的**對比

現在我們實際驗證非同步模式的切實有效,我會比較兩段 python 指令碼,這兩個指令碼除了sleep 方法外,其餘部分完全相同。在第乙個指令碼裡,我會用標準的 time.sleep 方法,在第二個指令碼裡使用 asyncio.sleep 的非同步方法。

這裡使用 sleep 是因為它是乙個用來展示非同步方法如何操作 i/o 的最簡單辦法。

使用同步 sleep 方法的**:

import asyncio   

import time

from datetime import datetime 

async def custom_sleep():   

print('sleep', datetime.now()) 

time.sleep(1) 

async def factorial(name, number):   

f = 1 

for i in range(2, number+1): 

print('task {}: compute factorial({})'.format(name, i)) 

await custom_sleep() 

f *= i 

print('task {}: factorial({}) is {}\n'.format(name, number, f)) 

start = time.time()   

loop = asyncio.get_event_loop() 

tasks = [   

asyncio.ensure_future(factorial("a", 3)), 

asyncio.ensure_future(factorial("b", 4)), 

] loop.run_until_complete(asyncio.wait(tasks))   

loop.close() 

end = time.time()   

print("total time: {}".format(end - start))  

指令碼輸出:

task a: compute factorial(2)   

sleep 2017-04-06 13:39:56.207479   

task a: compute factorial(3)   

sleep 2017-04-06 13:39:57.210128   

task a: factorial(3) is 6 

task b: compute factorial(2)   

sleep 2017-04-06 13:39:58.210778   

task b: compute factorial(3)   

sleep 2017-04-06 13:39:59.212510   

task b: compute factorial(4)   

sleep 2017-04-06 13:40:00.217308   

task b: factorial(4) is 24 

total time: 5.016386032104492  

使用非同步 sleep 的**:

import asyncio   

import time

from datetime import datetime 

async def custom_sleep():   

print('sleep {}\n'.format(datetime.now())) 

await asyncio.sleep(1) 

async def factorial(name, number):   

f = 1 

for i in range(2, number+1): 

print('task {}: compute factorial({})'.format(name, i)) 

await custom_sleep() 

f *= i 

print('task {}: factorial({}) is {}\n'.format(name, number, f)) 

start = time.time()   

loop = asyncio.get_event_loop() 

tasks = [   

asyncio.ensure_future(factorial("a", 3)), 

asyncio.ensure_future(factorial("b", 4)), 

] loop.run_until_complete(asyncio.wait(tasks))   

loop.close() 

end = time.time()   

print("total time: {}".format(end - start))  

指令碼輸出:

task a: compute factorial(2)   

sleep 2017-04-06 13:44:40.648665 

task b: compute factorial(2)   

sleep 2017-04-06 13:44:40.648859 

task a: compute factorial(3)   

sleep 2017-04-06 13:44:41.649564 

task b: compute factorial(3)   

sleep 2017-04-06 13:44:41.649943 

task a: factorial(3) is 6 

task b: compute factorial(4)   

sleep 2017-04-06 13:44:42.651755 

task b: factorial(4) is 24 

total time: 3.008226156234741  

從輸出可以看到,非同步模式的**執行速度快了大概兩秒。當使用非同步模式的時候(每次呼叫 await asyncio.sleep(1) ),程序控制權會返回到主程式的訊息迴圈裡,並開始執行佇列的其他任務(任務a或者任務b)。

當使用標準的 sleep方法時,當前執行緒會掛起等待。什麼也不會做。實際上,標準的 sleep 過程中,當前執行緒也會返回乙個 python 的直譯器,可以操作現有的其他執行緒,但這是另乙個話題了。

推薦使用非同步模式程式設計的幾個理由

很多公司的產品都廣泛的使用了非同步模式,如 facebook 旗下著名的 react native 和 rocksdb 。像 twitter 每天可以承載 50 億的使用者訪問,靠的也是非同步模式程式設計。所以說,通過**重構,或者改變模式方法,就能讓系統工作的更快,為什麼不去試一下呢?

python 非同步程式設計

但協程的特點在於是乙個執行緒執行,那和多執行緒比,協程有何優勢?最大的優勢就是協程極高的執行效率。因為子程式切換不是執行緒切換,而是由程式自身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯。第二大優勢就是不需要多執行緒的鎖機制,因為只有乙個執行緒,也不存在同時...

Python非同步程式設計

關於async和await python 3.5提供async和await,來實現非同步呼叫 有的庫提供非同步的函式修飾器,可以用庫介面,當然最好是用原生的async和await import asyncio async defhello num print hello format num awa...

nodeJS中的非同步程式設計

nodejs 不是單執行緒 在部落格專案中關於非同步問題 1.當使用者新增一條部落格時 需要通過post方式向伺服器傳送資料 後台獲取使用者以post方式拿到傳送過來的資料 然後存入資料庫 上面的 建立乙個空字串 當使用者向伺服器傳送請求時出發data事件將依次獲取來資料進行拼接 當使用者請求結束後...