一文速通Python并行計算:14 Python異步編程-協程的管理和調度

一文速通 Python 并行計算:14 Python 異步編程-協程的管理和調度

image

摘要:

Python 異步編程基于 async/await 構建協程,運行在事件循環中。協程生成 Task,遇到?await?時掛起,I/O 完成觸發回調恢復運行,通過事件循環非阻塞調度并發任務,實現單線程高并發。

image

關于我們更多介紹可以查看云文檔:Freak 嵌入式工作室云文檔,或者訪問我們的 wiki:****https://github.com/leezisheng/Doc/wik

原文鏈接:

FreakStudio的博客

往期推薦:

學嵌入式的你,還不會面向對象??!

全網最適合入門的面向對象編程教程:00 面向對象設計方法導論

全網最適合入門的面向對象編程教程:01 面向對象編程的基本概念

全網最適合入門的面向對象編程教程:02 類和對象的 Python 實現-使用 Python 創建類

全網最適合入門的面向對象編程教程:03 類和對象的 Python 實現-為自定義類添加屬性

全網最適合入門的面向對象編程教程:04 類和對象的Python實現-為自定義類添加方法

全網最適合入門的面向對象編程教程:05 類和對象的Python實現-PyCharm代碼標簽

全網最適合入門的面向對象編程教程:06 類和對象的Python實現-自定義類的數據封裝

全網最適合入門的面向對象編程教程:07 類和對象的Python實現-類型注解

全網最適合入門的面向對象編程教程:08 類和對象的Python實現-@property裝飾器

全網最適合入門的面向對象編程教程:09 類和對象的Python實現-類之間的關系

全網最適合入門的面向對象編程教程:10 類和對象的Python實現-類的繼承和里氏替換原則

全網最適合入門的面向對象編程教程:11 類和對象的Python實現-子類調用父類方法

全網最適合入門的面向對象編程教程:12 類和對象的Python實現-Python使用logging模塊輸出程序運行日志

全網最適合入門的面向對象編程教程:13 類和對象的Python實現-可視化閱讀代碼神器Sourcetrail的安裝使用

全網最適合入門的面向對象編程教程:全網最適合入門的面向對象編程教程:14 類和對象的Python實現-類的靜態方法和類方法

全網最適合入門的面向對象編程教程:15 類和對象的 Python 實現-__slots__魔法方法

全網最適合入門的面向對象編程教程:16 類和對象的Python實現-多態、方法重寫與開閉原則

全網最適合入門的面向對象編程教程:17 類和對象的Python實現-鴨子類型與“file-like object“

全網最適合入門的面向對象編程教程:18 類和對象的Python實現-多重繼承與PyQtGraph串口數據繪制曲線圖

全網最適合入門的面向對象編程教程:19 類和對象的 Python 實現-使用 PyCharm 自動生成文件注釋和函數注釋

全網最適合入門的面向對象編程教程:20 類和對象的Python實現-組合關系的實現與CSV文件保存

全網最適合入門的面向對象編程教程:21 類和對象的Python實現-多文件的組織:模塊module和包package

全網最適合入門的面向對象編程教程:22 類和對象的Python實現-異常和語法錯誤

全網最適合入門的面向對象編程教程:23 類和對象的Python實現-拋出異常

全網最適合入門的面向對象編程教程:24 類和對象的Python實現-異常的捕獲與處理

全網最適合入門的面向對象編程教程:25 類和對象的Python實現-Python判斷輸入數據類型

全網最適合入門的面向對象編程教程:26 類和對象的Python實現-上下文管理器和with語句

全網最適合入門的面向對象編程教程:27 類和對象的Python實現-Python中異常層級與自定義異常類的實現

全網最適合入門的面向對象編程教程:28 類和對象的Python實現-Python編程原則、哲學和規范大匯總

全網最適合入門的面向對象編程教程:29 類和對象的Python實現-斷言與防御性編程和help函數的使用

全網最適合入門的面向對象編程教程:30 Python的內置數據類型-object根類

全網最適合入門的面向對象編程教程:31 Python的內置數據類型-對象Object和類型Type

全網最適合入門的面向對象編程教程:32 Python的內置數據類型-類Class和實例Instance

全網最適合入門的面向對象編程教程:33 Python的內置數據類型-對象Object和類型Type的關系

全網最適合入門的面向對象編程教程:34 Python的內置數據類型-Python常用復合數據類型:元組和命名元組

全網最適合入門的面向對象編程教程:35 Python的內置數據類型-文檔字符串和__doc__屬性

全網最適合入門的面向對象編程教程:36 Python的內置數據類型-字典

全網最適合入門的面向對象編程教程:37 Python常用復合數據類型-列表和列表推導式

全網最適合入門的面向對象編程教程:38 Python常用復合數據類型-使用列表實現堆棧、隊列和雙端隊列

全網最適合入門的面向對象編程教程:39 Python常用復合數據類型-集合

全網最適合入門的面向對象編程教程:40 Python常用復合數據類型-枚舉和enum模塊的使用

全網最適合入門的面向對象編程教程:41 Python常用復合數據類型-隊列(FIFO、LIFO、優先級隊列、雙端隊列和環形隊列)

全網最適合入門的面向對象編程教程:42 Python常用復合數據類型-collections容器數據類型

全網最適合入門的面向對象編程教程:43 Python常用復合數據類型-擴展內置數據類型

全網最適合入門的面向對象編程教程:44 Python內置函數與魔法方法-重寫內置類型的魔法方法

全網最適合入門的面向對象編程教程:45 Python實現常見數據結構-鏈表、樹、哈希表、圖和堆

全網最適合入門的面向對象編程教程:46 Python函數方法與接口-函數與事件驅動框架

全網最適合入門的面向對象編程教程:47 Python函數方法與接口-回調函數Callback

全網最適合入門的面向對象編程教程:48 Python函數方法與接口-位置參數、默認參數、可變參數和關鍵字參數

全網最適合入門的面向對象編程教程:49 Python函數方法與接口-函數與方法的區別和lamda匿名函數

全網最適合入門的面向對象編程教程:50 Python函數方法與接口-接口和抽象基類

全網最適合入門的面向對象編程教程:51 Python函數方法與接口-使用Zope實現接口

全網最適合入門的面向對象編程教程:52 Python函數方法與接口-Protocol協議與接口

全網最適合入門的面向對象編程教程:53 Python字符串與序列化-字符串與字符編碼

全網最適合入門的面向對象編程教程:54 Python字符串與序列化-字符串格式化與format方法

全網最適合入門的面向對象編程教程:55 Python字符串與序列化-字節序列類型和可變字節字符串

全網最適合入門的面向對象編程教程:56 Python字符串與序列化-正則表達式和re模塊應用

全網最適合入門的面向對象編程教程:57 Python字符串與序列化-序列化與反序列化

全網最適合入門的面向對象編程教程:58 Python字符串與序列化-序列化Web對象的定義與實現

全網最適合入門的面向對象編程教程:59 Python并行與并發-并行與并發和線程與進程

一文速通Python并行計算:00 并行計算的基本概念

一文速通Python并行計算:01 Python多線程編程-基本概念、切換流程、GIL鎖機制和生產者與消費者模型

一文速通Python并行計算:02 Python多線程編程-threading模塊、線程的創建和查詢與守護線程

一文速通Python并行計算:03 Python多線程編程-多線程同步(上)—基于互斥鎖、遞歸鎖和信號量

一文速通Python并行計算:04 Python多線程編程-多線程同步(下)—基于條件變量、事件和屏障

一文速通Python并行計算:05 Python多線程編程-線程的定時運行

一文速通Python并行計算:06 Python多線程編程-基于隊列進行通信

一文速通Python并行計算:07 Python多線程編程-線程池的使用和多線程的性能評估

一文速通Python并行計算:08 Python多進程編程-進程的創建命名、獲取ID、守護進程的創建和終止進程

一文速通Python并行計算:09 Python多進程編程-進程之間的數據同步-基于互斥鎖、遞歸鎖、信號量、條件變量、事件和屏障

一文速通Python并行計算:10 Python多進程編程-進程之間的數據共享-基于共享內存和數據管理器

一文速通Python并行計算:11 Python多進程編程-進程之間的數據安全傳輸-基于隊列和管道

一文速通Python并行計算:12 Python多進程編程-進程池Pool

一文速通Python并行計算:13 Python異步編程-基本概念與事件循環和回調機制

更多精彩內容可看:

C語言一點五編程實戰:純 C 的模塊化×繼承×多態框架

給你的 Python 加加速:一文速通 Python 并行計算

一文搞懂 CM3 單片機調試原理

肝了半個月,嵌入式技術棧大匯總出爐

電子計算機類比賽的“武林秘籍”

一個MicroPython的開源項目集錦:awesome-micropython,包含各個方面的Micropython工具庫

Avnet ZUBoard 1CG開發板—深度學習新選擇

工程師不要迷信開源代碼,還要注重基本功

什么?配色個性化的電機驅動模塊?!!

什么?XIAO主控新出三款擴展板!

手把手教你實現Arduino發布第三方庫

萬字長文手把手教你實現MicroPython/Python發布第三方庫

一文速通電子設計大賽,電子人必看的獲獎秘籍

一文速通光電設計大賽,電子人必看!

工科比賽“無腦”操作指南:知識學習硬件選購→代碼調試→報告撰寫的保姆級路線圖

單場會議拍攝收費6000+?拍攝技巧和步驟都在這里

0基礎如何沖擊大唐杯國獎?學姐的的備賽心得都在這里

爆肝整理長文】大學生競賽速通指南:選題 × 組隊 × 路演 48 小時備賽搞定

當代大學生競賽亂象:從“內卷”到“祖傳項目”的生存指南

女大學生擺攤虧損5000元踩點實錄:成都哪里人最多、最容易賺到錢?我告訴你!

用拍立得在成都網紅打卡點賺錢:一份超實用地攤級旅游副業教程

成都印象:一個電子女孩親手做了點浪漫

普通繼電器 vs 磁保持繼電器 vs MOS管:工作原理與電路設計全解析

告別TP4056!國產SY3501D單芯片搞定充放電+升壓,僅需7個元件!附開源PCB文件

POB面向老板編程—現實驅動的新型編程范式

文檔獲取:

可訪問如下鏈接進行對文檔下載:

https://github.com/leezisheng/Doc

該文檔是一份關于 并行計算Python 并發編程 的學習指南,內容涵蓋了并行計算的基本概念、Python 多線程編程、多進程編程以及協程編程的核心知識點:

image

正文

在上文提到的例子中,我們看到當一個程序變得很大而且復雜時,將其劃分為子程序,每一部分實現特定的任務是個不錯的方案。子程序不能單獨執行,只能在主程序的請求下執行,主程序負責協調使用各個子程序。協程就是子程序的泛化。和子程序一樣的事,協程只負責計算任務的一步;和子程序不一樣的是,協程沒有主程序來進行調度。這是因為協程通過管道連接在一起,沒有監視函數負責順序調用它們。

對于子程序來說,調用是通過棧實現的,子程序調用總是一個入口,一次返回,調用順序是明確的。比如 A 調用 B,B 在執行過程中又調用了 C,C 執行完畢返回,B 執行完畢返回,最后是 A 執行完畢。協程看上去也是子程序,但執行過程中,在子程序內部可中斷,然后轉而執行別的子程序,在適當的時候再返回來接著執行。從心理學的角度看,協程類似于人類的“任務切換”能力,我們可以暫時將注意力從一個任務轉移到另一個,然后再回來繼續未完成的任務。

或者說,**在協程中,執行點可以被掛起,可以被從之前掛起的點恢復執行。**通過協程池就可以插入到計算中:運行第一個任務,直到它返回(yield)執行權,然后運行下一個,這樣順著執行下去。這種插入的控制組件就是前文介紹的事件循環。它持續追蹤所有的協程并執行它們。

協程的另外一些重要特性如下:

  • 協程可以有多個入口點,并可以 yield 多次;
  • 協程可以將執行權交給其他協程

yield 表示協程在此暫停,并且將執行權交給其他協程。因為協程可以將值與控制權一起傳遞給另一個協程,所以“yield 一個值”就表示將值傳給下一個執行的協程。

協程與傳統的線程或進程相比,有幾個關鍵區別:

**(1)輕量級:**協程通常是用戶態的,子程序切換(函數)不是線程切換,由程序自身控制,切換開銷比系統線程小得多。

**(2)非搶占式:**協程的切換是協作式的,即協程需要顯式地 yield 來讓出控制權。

**(3)更好的控制:**協程提供了更細粒度的控制,如何以及何時切換是由程序員或協程庫決定的。

協程可以處理 IO 密集型程序的效率問題,但是處理 CPU 密集型不是它的長處,如要充分發揮 CPU 利用率可以結合多進程 + 協程。

1.使用 Asyncio 管理協程

Python3.x 提供了如下方式實現協程:

  • asyncio + yield from (python3.4+) :

asyncio 是 Python3.4 版本引入的標準庫,直接內置了對異步 IO 的支持。asyncio 的異步操作,需要在 coroutine 中通過 yield from 完成。

import asyncio@asyncio.coroutine
def test(i):print('test_1', i)r = yield from asyncio.sleep(1)print('test_2', i)if __name__ == '__main__':loop = asyncio.get_event_loop()tasks = [test(i) for i in range(3)]loop.run_until_complete(asyncio.wait(tasks))loop.close()

asyncio.coroutine 使用裝飾器,定義了一個協程。所謂裝飾器是給現有的模塊增添新的小功能的函數,它可以對原函數進行功能擴展,而且還不需要修改原函數的內容,也不需要修改原函數的調用。

使用 @asyncio.coroutine 定義協程的通用方法如下:

import asyncio@asyncio.coroutine
def coroutine_function(function_arguments):_# DO_SOMETHING_

以上代碼將 test(i) 定義為一個協程,然后就把這個協程放到事件循環中執行。test(i) 首先執行打印操作,這里用 asyncio.sleep(1) 模擬一個耗時 1 秒的 IO 操作,asyncio.sleep() 本身也是一個協程,這里使用 yield from 語法調用 asyncio.sleep(),但注意線程不會等待 asyncio.sleep(),而是直接中斷并執行下一個消息循環。當 asyncio.sleep() 返回時,線程就可以從 yield from 拿到返回值(此處是 None),然后接著執行下一行語句。由此實現異步執行。

  • asyncio + async/await (python3.5+):

為了簡化并更好地標識異步 IO,從 Python3.5 開始引入了新的語法 async 和 await,可以讓 coroutine 的代碼更簡潔易讀。請注意,async 和 await 是 coroutine 的新語法,使用新語法只需要做兩步簡單的替換:

  • 把 @asyncio.coroutine 替換為 async;
  • 把 yield from 替換為 await,即讓出當前的執行權,等待的對象有結果返回時,再重新獲得可以被繼續執行的權利,只有可等待對象才能被 await。

注意,包含 @asyncio.coroutine 裝飾器的將從 Python3.11 中刪除,因此 asyncio 模塊沒有 @asyncio.coroutine 裝飾符。

import asyncio
async def test(i):print('test_1', i)await asyncio.sleep(1)print('test_2', i)if __name__ == '__main__':loop = asyncio.new_event_loop()asyncio.set_event_loop(loop)_# 在python3.8后直接把協程對象傳給asyncio.wait()是不行的,__# 必須封裝成tasks對象傳入_tasks = [loop.create_task(test(1)),loop.create_task(test(2)),loop.create_task(test(3))]loop.run_until_complete(asyncio.wait(tasks))loop.close()

image

除了 await 方法,asyncio 提供了**asyncio.run()**來執行協程:

run() 函數接收一個協程對象,在執行時,總會**創建一個新的事件循環,并在結束后關閉循環。****理想情況下,****run() ****函數應當被作為程序的總入口,并且只會被調用一次。**如果同一線程中還有其它事件循環在運行,則此方法不能被調用。

image

2.使用 Asyncio 控制任務

Asyncio 是用來處理事件循環中的異步進程和并發任務執行的。**它還提供了 asyncio.Task() 類,可以在任務中使用協程。**它的作用是,在同一事件循環中,**運行某一個任務的同時可以并發地運行多個任務。當協程被包在任務中,它會自動將任務和事件循環連接起來,當事件循環啟動的時候,任務自動運行。**這樣就提供了一個可以自動驅動協程的機制。

image

2.1 asyncio.create_task()

image

下面的例子中我們使用 asyncio.create_task() 創建 Task,create_task() 會把一個協程打包成一個任務(Task),并立即排入日程準備執行,函數返回值是打包完成的 Task 對象。

import asyncio
import time
async def foo(n):await asyncio.sleep(n)async def main():task1 = asyncio.create_task(foo(1))task2 = asyncio.create_task(foo(2))t1 = time.time()print('hello')await task1await task2print('coroutine')t2 = time.time()print('cost:', t2 - t1)asyncio.run(main())

如下為運行結果:

image

當使用 create_task() 時,創建的任務立即被加入到事件循環中,并不會阻塞當前的程序,所以上述程序在打印出 hello 后只需等待 2 秒就打印出 coroutine

2.2 asyncio.gather()

asyncio.gather() 函數允許調用者將多個可等待對象組合在一起。分組后,可等待對象可以并發執行、等待和取消。它是一個有用的實用函數,可用于分組和執行多個協程或多個任務。

image

從功能上看,asyncio.waitasyncio.gather 實現的效果是相同的,都是把所有 Task 任務結果收集起來。但不同的是,asyncio.wait 會返回兩個值:donependingdone 為已完成的協程 Taskpending 為超時未完成的協程 Task,需通過 future.result 調用 Taskresult;而 asyncio.gather返回的是所有已完成Taskresult**,不需要再進行調用或其他操作,就可以得到全部結果。**

import asyncioasync def foo():return 'foo'
async def bar():raise RuntimeError('fake runtime error')
async def main():task1 = asyncio.create_task(foo())task2 = asyncio.create_task(bar())_# return_exceptions=True__# 如果return_exceptions為True,__# 異常會和成功的結果一樣處理,并聚合至結果列表_results = await asyncio.gather(task1, task2, return_exceptions=True)print(results)_# 返回結果的順序和傳參順序一致__# isinstance() 函數來判斷一個對象是否是一個已知的類型__# isinstance(object, classinfo)_assert isinstance(results[1], RuntimeError)try:_# 如果return_exceptions為False(默認)__# 所引發的首個異常會立即傳播給等待gather()的任務_results = await asyncio.gather(task1, task2, return_exceptions=False)_# 此處打印并不會被執行, results 也未被賦值_print(results)except RuntimeError as runtime_err:_# 捕獲異常并打印: fake runtime error_print(runtime_err)asyncio.run(main())

執行結果如下:

image

2.3 asyncio.wait()

asyncio.wait() 函數可用于等待一組異步任務完成,回想一下,asyncio 任務是包裝協程的 asyncio.Task 類的一個實例。它允許獨立調度和執行協程,Task 實例提供任務句柄以查詢狀態和獲取結果。**wait()**函數允許我們等待一組任務完成。等待調用可以配置為等待不同的條件,例如所有任務完成、第一個任務完成以及第一個任務因錯誤而失敗。

image

asyncio.wait 最常見的寫法是:await asyncio.wait(task_list),表示運行直到所有給定的協程都完成。常見寫法為:

...
_# create many tasks_
tasks = [asyncio.create_task(task_coro(i)) for i in range(10)]

示例代碼如下:

import asyncioasync def foo():await asyncio.sleep(3)return 'foo'
async def bar():await asyncio.sleep(1)return 'bar'
async def main():task1 = asyncio.create_task(foo())task2 = asyncio.create_task(bar())_# 有一個任務執行完成即返回, 總共耗時 1 秒_done, pending = await asyncio.gather(task1, task2, return_exceptions=True)_# done 集合里包含打包成 Task 的 bar()_print(f'done: {done}')_# pendding 集合里包含打包成 Task 的 foo()_print(f'pending: {pending}')_# 所有任務執行完成后返回, 總共耗時 3 秒_done, pending = await asyncio.gather(task1, task2, return_exceptions=True)_# done 集合里包含被帶打包成 Task 的 foo() 和 bar()_print(f'done: {done}')_# pending 集合為空_print(f'pending: {pending}')_# 所有任務執行完成, 但運行時間不能超 2 秒后返回, 總共耗時 2 秒_done, pending = await asyncio.gather(task1, task2, return_exceptions=True)_# done 集合里包含打包成 Task 的 bar()_print(f'done: {done}')_# pendding 集合里包含打包成 Task 的 foo()_print(f'pending: {pending}')
asyncio.run(main())

image

2.4 asyncio.as_completed()

有時,我們必須在完成一個后臺任務后立即開始下面的動作。比如我們爬取一些數據,馬上調用機器學習模型進行計算,gather 方法不能滿足我們的需求,但是我們可以使用 as_completed 方法。

image

as_completed 不是并發方法,接受 aws 集合,返回一個帶有 yield 語句的迭代器。所以我們可以直接遍歷每個完成的后臺任務,我們可以對每個任務單獨處理異常,而不影響其他任務的執行:

示例代碼如下:

import asyncioasync def foo():await asyncio.sleep(2)return 'foo'async def bar():await asyncio.sleep(1)return 'bar'async def main():for fut in asyncio.as_completed({foo(), bar()}):earliest_result = await fut_# 會依次打印 bar 和 foo, 因為 bar() 會更早執行完畢_print(earliest_result)asyncio.run(main())

image

以上介紹多任務并發時引入了超時的概念,超時也可以被應用在單獨的一個任務中,使用 asyncio.wait_for(aw, timeout) 函數,該函數接受一個任務 aw 和超時時間 timeout,如果在限制時間內完成,則會正常返回,否則會被取消并拋出 asyncio.TimeoutError 異常。

為了防止任務被取消,可以使用 asyncio.shield(aw) 進行保護。shield() 會屏蔽外部取消操作,如果外部任務被取消,其內部正在執行的任務不會被取消,在內部看來取消操作并沒有發生,由于內部仍正常執行,執行完畢后會觸發異常,如果確保程序能忽略異常繼續執行,需要在外部使用 try-except 捕獲異常。如果在任務內部取消,則會被成功取消。

Asyncio 模塊為我們提供了 asyncio.Task(coroutine) 方法來處理計算任務,它可以調度協程的執行。任務對協程對象在事件循環的執行負責。如果被包裹的協程要從 future(就是協程的實例化對象)調度,那么任務會被掛起,等待 future 的計算結果。

future 計算完成,被包裹的協程將會拿到 future 返回的結果或異常繼續執行。另外,需要注意的是,事件循環一次只能運行一個任務,除非還有其它事件循環在不同的線程并行運行,此任務才有可能和其他任務并行。當一個任務在等待 future 執行的期間,事件循環會運行一個新的任務。

在下面的代碼中,我們展示了三個可以被 Asyncio.Task() 并發執行的數學函數,在這個例子中,我們定義了三個協程, factorial, fibonaccibinomialCoeff ,為了能并行執行這三個任務,我們將其放到一個 tasklist 中得到事件循環然后運行任務,最后,關閉事件循環。

import asyncioasync def factorial(number):f = 1for i in range(2, number + 1):print("Asyncio.Task: Compute factorial(%s)" % (i))await asyncio.sleep(1)f *= iprint("Asyncio.Task - factorial(%s) = %s" % (number, f))async def fibonacci(number):a, b = 0, 1for i in range(number):print("Asyncio.Task: Compute fibonacci (%s)" % (i))await asyncio.sleep(1)a, b = b, a + bprint("Asyncio.Task - fibonacci(%s) = %s" % (number, a))async def binomialCoeff(n, k):result = 1for i in range(1, k+1):result = result * (n-i+1) / iprint("Asyncio.Task: Compute binomialCoeff (%s)" % (i))await asyncio.sleep(1)print("Asyncio.Task - binomialCoeff(%s , %s) = %s" % (n, k, result))if __name__ == "__main__":tasks = [asyncio.Task(factorial(10)),asyncio.Task(fibonacci(10)),asyncio.Task(binomialCoeff(20, 10))]loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))loop.close()

運行結果如下:

image

3.使用 Asyncio 和 Futures

future 是一個 Python 對象,它包含一個你希望在未來某個時間點獲得、但目前還不存在的值。通常,當創建 future 時,它沒有任何值,因為它還不存在。在這種狀態下,它被認為是不完整的、未解決的或根本沒有完成的。然后一旦你得到一個結果,就可以設置 future 的值,這將完成 future。那時,我們可以認為它已經完成,并可從 future 中提取結果。

image

要操作 Asyncio 中的 Future ,必須進行以下聲明:

import asyncio
future = asyncio.Future()

基本的方法有:

方法作用
cancel()取消 future 的執行,調度回調函數
result()返回 future 代表的結果
exception()返回 future 中的 Exception
add_done_callback(fn)添加一個回調函數,當 future 執行的時候會調用這個回調函數
remove_done_callback(fn)從“call whten done”列表中移除所有 callback 的實例
set_result(result)將 future 標為執行完成,并且設置 result 的值
set_exception(exception)將 future 標為執行完成,并設置 Exception
import asyncio_# asyncio 里面有一個類 Future,實例化之后即可得到 future 對象_
_# 然后 asyncio 里面還有一個類 Task,實例化之后即可得到 task 對象(也就是任務)_
_# 這個 Task 是 Future 的子類,所以我們用的基本都是 task 對象,而不是 future 對象_
_# 但 Future 這個類和 asyncio 的實現有著密不可分的關系,所以我們必須單獨拿出來說_future = asyncio.Future()
print(future)  _# <Future pending>_
print(future.__class__)  _# <class '_asyncio.Future'>_
print(f"future 是否完成: {future.done()}")  _# future 是否完成: False__# 設置一個值,通過 set_result_
future.set_result("古明地覺")
print(f"future 是否完成: {future.done()}")  _# future 是否完成: True_
print(future)  _# <Future finished result='古明地覺'>_
print(f"future 的返回值: {future.result()}")  _# future 的返回值: 古明地覺_

可通過調用其類型對象 Future 來創建 future,此時 future 上將沒有結果集,因此調用其 done 方法將返回 False。此后用 set_result 方法設置 future 的值,這將把 future 標記為已完成。或者,如果想在 future 中設置一個異常,可調用 set_exception(必須在調用set_result(設置結果)之后才能調用result(獲取結果),并且set_result只能調用一次,但result可以調用多次)

在下面的示例代碼中,我們定義了一個函數 make_request,該函數里面創建了一個 future 和一個任務,該任務將在 1 秒后異步設置 future 的結果。然后在主函數中調用 make_request,當調用它時,將立即得到一個沒有結果的 future。然后 await future 會讓主協程陷入等待,并將執行權交出去。一旦當 future 有值了,那么再恢復 main() 協程,拿到返回值進行處理。

import asyncioasync def set_future_value(future):await asyncio.sleep(1)future.set_result("Hello World")
def make_request():future = asyncio.Future()_# 創建一個任務來異步設置 future 的值_asyncio.create_task(set_future_value(future))return future
async def main():_# 注意這里的 make_request,它是一個普通的函數,如果在外部直接調用肯定是會報錯的__# 因為沒有事件循環,在執行 set_future_value 時會報錯__# 但如果在協程里面調用是沒問題的,因為協程運行時,事件循環已經啟動了__# 此時在 make_request 里面,會啟動一個任務_future = make_request()print(f"future 是否完成: {future.done()}")_# 阻塞等待,直到 future 有值,什么時候有值呢?__# 顯然是當協程 set_future_value 里面執行完 future.set_result 的時候_value = await future  _# 暫停 main(),直到 future 的值被設置完成_print(f"future 是否完成: {future.done()}")print(value)
asyncio.run(main())

image

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918989.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918989.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918989.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Node.js面試題及詳細答案120題(16-30) -- 核心模塊篇

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

RabbitMQ:Windows版本安裝部署

目錄一、概述二、OPT三、安裝RabbitMQ四、登錄測試一、概述 什么是MQ&#xff0c;有什么做作用&#xff1f; MQ即MessageQueue&#xff0c;消息隊列。可以分為兩部分理解&#xff1a;消息Message用于在不同的應用程序中傳遞數據。隊列Queue&#xff0c;一種FIFO先進先出的數據…

酒店行業安全體系構建與優化策略

酒店行業安全體系構建與優化策略為確保酒店行業領導及賓客的安全&#xff0c;構建全面的治安聯防體系及事故處理預案至關重要。某招待所通過設立保衛部&#xff0c;細化內保、治安、防火及交通管理職能&#xff0c;并下設警衛班、監控中心和電瓶車班&#xff0c;以全方位保障安…

python30-正則表達式

在Python中需要通過正則表達式對字符串進?匹配的時候&#xff0c;可以使??個python自帶的模塊&#xff0c;名字為re。 re模塊的使用&#xff1a;import re 一、匹配函數 1-1、re.match函數&#xff1a;返回匹配對象 match函數實現的是精準匹配&#xff0c;嘗試從字符串的…

EP1C12F324I7N Altera Cyclone FPGA

EP1C12F324I7N 是 阿爾特拉 Altera Cyclone 系列中的一款 SRAM-based FPGA&#xff0c;定位為低成本、低功耗、面向嵌入式與消費/工業類量產應用的器件。該器件提供約 12,060 個邏輯單元&#xff08;Logic Elements&#xff09;&#xff0c;片上嵌入式存儲約 234 kbit&#xff…

html5語義元素

1、參考&#xff1a;HTML5 語義元素 | 菜鳥教程 2、實戰 HTML5 <section> 元素 <section> 標簽定義文檔中的節&#xff08;section、區段&#xff09;。比如章節、頁眉、頁腳或文檔中的其他部分。 根據W3C HTML5文檔: section 包含了一組內容及其標題。 <!D…

java調用PyTorch 訓練模型實現神經網絡全流程

以下是完整的操作流程:用 PyTorch 訓練模型 → 導出為 ONNX 格式 → 用 Java 加載并推理,兼顧開發效率(PyTorch 快速訓練)和生產部署(Java 穩定運行)。 一、PyTorch 訓練模型并導出為 ONNX 1. 安裝依賴 bash pip install torch onnx # PyTorch 和 ONNX 庫2. 訓練一個…

Maven - Spring Boot 項目打包本地 jar 的 3 種方法

文章目錄Pre概述方案思路構建流程圖工作機制說明目錄結構示例POM 配置模板構建與驗證注意事項方案優缺點Pre Maven - Manual Maven JAR Installation&#xff1a;用 mvn install:install-file 安裝本地 JAR 的實用指南 概述 在 Spring Boot 項目中&#xff0c;通常依賴包會從…

平替 Claude Code,API接入 GPT-5,Codex CLI 國內直接使用教程

最新升級接入GPT-5的 Codex 擁有可以媲美 Claude Code 的AI編碼能力&#xff0c;本文將指導你在 Windows系統上部署原生的 Codex CLI程序&#xff0c;并且接入超低價中轉API&#xff0c;讓你在國內直接用上超高性價比的 OpenAI Codex CLI 應用。關于 CodexCodex 是 OpenAI 開發…

kubernertes (K8S)部署

參考&#xff1a; https://blog.csdn.net/yu33575/article/details/135387548 二進制安裝k8s&#xff1a; https://blog.csdn.net/qq_73990369/article/details/143217084 K8S二進制安裝與部署 &#xff1a;https://blog.csdn.net/fantuan_sss/article/details/139073366 k8s…

LeetCode 簡單JS刷題

目錄 返回數組最后一個元素 2787.將一個數字表示成冪的和的方案數 326.3的冪 1780.判斷一個數字是否可以表示成三的冪的和 342.4的冪 返回數組最后一個元素 1.請你編寫一段代碼實現一個數組方法&#xff0c;使任何數組都可以調用 array.last() 方法&#xff0c;這個方法將…

七大排序算法全解析:從入門到精通

目錄 一.排序的概念 二.常見排序算法的實現 2.1 插入排序 &#xff08;1&#xff09;直接插入排序&#xff1a; 當插入第i(i>1)個元素時&#xff0c;前面的array[0],array[1],…,array[i-1]已經排好序&#xff0c;此時用array[i]的排序碼與array[i-1],array[i-2],…的排序…

20250814在榮品RD-RK3588開發板的Android13下解決卡迪的LCD屏在開機的時候brightness最暗【背光的pwm信號的極性反了】

20250814在榮品RD-RK3588開發板的Android13下解決卡迪的LCD屏在開機的時候brightness最暗【背光的pwm信號的極性反了】 2025/8/14 11:33緣起&#xff1a;在榮品RD-RK3588開發板的Android13下&#xff0c;卡迪的LCD屏在開機的時候很暗&#xff0c;幾乎看不見。 在命令行查看亮度…

Flink的狀態管理

一、狀態的概念Flink的狀態其實你就可以將其想象為中間結果就可以了。在Flink中&#xff0c;算子的任務可以分為無狀態和有狀態兩種情況。無狀態算子任務在計算過程中是不依賴于其他數據的&#xff0c;只根據當前的輸入數據就可以得到結果輸出。比如之前講到的Map、FlatMap、Fi…

GoLand 項目從 0 到 1:第八天 ——GORM 命名策略陷阱與 Go 項目啟動慢問題攻堅

第八天核心任務&#xff1a;解決開發中的兩大技術卡點今天的開發不僅聚焦于代碼層面的數據庫字段映射問題&#xff0c;還遭遇了一個困擾團隊許久的環境難題 ——Go 項目啟動異常緩慢。經過多維度排查&#xff0c;我們不僅理清了 GORM 命名策略的設計邏輯&#xff0c;還找到了影…

在Ubuntu上安裝Google Chrome的詳細教程

步驟 1&#xff1a;下載 Google Chrome 安裝包 打開瀏覽器輸入https://www.google.cn/chrome/&#xff0c;然后進入Chrome瀏覽器官方網站 點擊下載選擇Debian/Ubuntu版本 google-chrome-stable_current_amd64.deb步驟 2&#xff1a;安裝下載的.deb 包 sudo dpkg -i google-chro…

el-table合并相同名稱的行

el-table合并相同名稱的行 <template><el-table:data"tableData":span-method"objectSpanMethod"border><el-table-columnprop"name"label"名稱"width"180"></el-table-column><el-table-column…

解決 VSCode 無法從右鍵菜單“通過 Code 打開”文件夾的問題

&#x1f9e9; 一、問題現象 VSCode 已安裝&#xff0c;但右鍵文件夾/桌面空白處無“通過 Code 打開在 VSCode 中執行 Shell Command: Install ‘Open with Code’ 無反應手動添加后菜單顯示亂碼&#xff08;如 €?? Code ‰“€&#xff09;點擊右鍵菜單無響應或提示“找不到…

服務器數據恢復—服務器硬盤狀態燈變紅,分區數據恢復過程

服務器數據恢復環境&故障&#xff1a; 某公司服務器上有一組由3塊硬盤組建的raid5磁盤陣列。 服務器上1塊硬盤的狀態燈變為紅色&#xff0c;磁盤陣列出現故障&#xff0c;分區無法識別。服務器數據恢復過程&#xff1a; 1、將故障服務器上所有磁盤編號后取出。經過初檢&…

MySQL → SQL → DDL → 表操作 → 數據類型 知識鏈整理成一份系統的內容

1. 知識結構MySQL└── SQL&#xff08;結構化查詢語言&#xff09;├── DDL&#xff08;數據定義語言&#xff09; → 定義結構│ ├── 表操作&#xff08;創建/修改/刪除表&#xff09;│ └── 數據類型&#xff08;列字段類型定義&#xff09;├── DML&…