您好,登錄后才能下訂單哦!
本篇文章為大家展示了Python中怎么利用Asyncio實現(xiàn)異步編程,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
異步是怎么一回事?
在傳統(tǒng)的順序編程中, 所有發(fā)送給解釋器的指令會一條條被執(zhí)行。此類代碼的輸出容易顯現(xiàn)和預(yù)測。 但是…
譬如說你有一個腳本向3個不同服務(wù)器請求數(shù)據(jù)。 有時,誰知什么原因,發(fā)送給其中一個服務(wù)器的請求可能意外地執(zhí)行了很長時間。想象一下從第二個服務(wù)器獲取數(shù)據(jù)用了10秒鐘。在你等待的時候,整個腳本實際上什么也沒干。如果你可以寫一個腳本可以不去等待第二個請求而是僅僅跳過它,然后開始執(zhí)行第三個請求,然后回到第二個請求,執(zhí)行之前離開的位置會怎么樣呢。就是這樣。你通過切換任務(wù)最小化了空轉(zhuǎn)時間。盡管如此,當(dāng)你需要一個幾乎沒有I/O的簡單腳本時,你不想用異步代碼。
還有一件重要的事情要提,所有代碼在一個線程中運行。所以如果你想讓程序的一部分在后臺執(zhí)行同時干一些其他事情,那是不可能的。
準(zhǔn)備開始
這是 asyncio 主概念最基本的定義:
協(xié)程 — 消費數(shù)據(jù)的生成器,但是不生成數(shù)據(jù)。Python 2.5 介紹了一種新的語法讓發(fā)送數(shù)據(jù)到生成器成為可能。我推薦查閱David Beazley “A Curious Course on Coroutines and Concurrency” 關(guān)于協(xié)程的詳細介紹。
任務(wù) — 協(xié)程調(diào)度器。如果你觀察下面的代碼,你會發(fā)現(xiàn)它只是讓 event_loop 盡快調(diào)用它的_step ,同時 _step 只是調(diào)用協(xié)程的下一步。
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)
事件循環(huán) — 把它想成 asyncio 的中心執(zhí)行器。
現(xiàn)在我們看一下所有這些如何融為一體。正如我之前提到的,異步代碼在一個線程中運行。
從上圖可知:
1.消息循環(huán)是在線程中執(zhí)行
2.從隊列中取得任務(wù)
3.每個任務(wù)在協(xié)程中執(zhí)行下一步動作
4.如果在一個協(xié)程中調(diào)用另一個協(xié)程(await <coroutine_name>),會觸發(fā)上下文切換,掛起當(dāng)前協(xié)程,并保存現(xiàn)場環(huán)境(變量,狀態(tài)),然后載入被調(diào)用協(xié)程
5.如果協(xié)程的執(zhí)行到阻塞部分(阻塞I/O,Sleep),當(dāng)前協(xié)程會掛起,并將控制權(quán)返回到線程的消息循環(huán)中,然后消息循環(huán)繼續(xù)從隊列中執(zhí)行下一個任務(wù)...以此類推
6.隊列中的所有任務(wù)執(zhí)行完畢后,消息循環(huán)返回***個任務(wù)
異步和同步的代碼對比
現(xiàn)在我們實際驗證異步模式的切實有效,我會比較兩段 python 腳本,這兩個腳本除了sleep 方法外,其余部分完全相同。在***個腳本里,我會用標(biāo)準(zhǔn)的 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
上述內(nèi)容就是Python中怎么利用Asyncio實現(xiàn)異步編程,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。