深入淺出 Python Asynchronous I/O:從 asyncio 入門到實戰

在現代軟件開發中,性能是一個永恒的話題。特別是在處理網絡請求、文件讀寫等 I/O 密集型任務時,傳統的同步編程模型可能會因為等待而浪費大量時間。為了解決這個問題,異步編程應運而生。Python 通過內置的 asyncio 庫,為開發者提供了強大而優雅的異步編程能力。 [1][2]

本文將帶你從零開始,逐步深入 asyncio 的世界,理解其核心概念,并最終通過實戰案例掌握其用法。

1. 什么是異步編程?為什么要用它?

想象一下你在廚房做飯,需要同時燒水、切菜和炒菜。

  • 同步 (Synchronous):你先把水壺放到灶上,然后就一直盯著它,直到水燒開。之后,你再去切菜,切完所有菜后,最后才開始炒菜。在這個過程中,當你在等待水燒開時,你什么也做不了,時間被白白浪費。
  • 異步 (Asynchronous):你把水壺放到灶上后,就不管它了,直接去切菜。切菜的間隙,你抽空看一眼水開了沒。水一開,你就去處理。這樣,等待水燒開的時間被你用來切菜,整個做飯的效率大大提高。 [3]

代碼的世界也是如此。同步編程就是一次只做一件事,必須等前一件事(比如一次網絡請求)完成后才能做下一件。 [1] 而異步編程允許程序在等待一個耗時操作(通常是 I/O 操作)時,切換去執行其他任務,從而提高整體效率。 [1][3]

asyncio 正是 Python 用于實現這種高效工作模式的標準庫,它特別適合 I/O 密集型和高層級的網絡代碼。 [3]

2. asyncio 的核心基石:async/await 與協程

要使用 asyncio,首先需要理解幾個關鍵概念。

協程 (Coroutine)

在 Python 中,使用 async def 關鍵字定義的函數,我們稱之為協程函數。調用它并不會立即執行函數體,而是會返回一個協程對象。 [4] 協程可以被看作是一種可以暫停和恢復執行的特殊函數。 [1]

await

這個關鍵字只能在 async def 函數內部使用。它的作用是“等待”一個可等待對象 (Awaitable) 執行完成。 [5] 可等待對象包括協程、任務 (Task) 和 Future 對象。 [5] 當程序執行到 await 時,它會告訴事件循環:“這個操作有點耗時,我先在這里暫停,你可以去忙別的,等我好了再回來繼續。” [6]

事件循環 (Event Loop)

事件循環是 asyncio 的心臟。 [2][6] 你可以把它想象成一個大管家,負責調度和執行所有的異步任務。它會不斷檢查是否有任務已經準備好可以繼續運行,或者是否有新的任務需要開始。 [2]

asyncio.run()

這是啟動異步程序的入口。它會創建一個新的事件循環,運行你傳入的頂級協程(通常是 main 函數),并在協程執行完畢后關閉事件循環。 [7][8]

3. 牛刀小試:你的第一個 asyncio 程序

讓我們來看一個最簡單的例子。

import asyncio
import time# 使用 async def 定義一個協程函數
async def say_hello(delay, message):"""一個簡單的協程,會延遲指定秒數后打印消息。"""print(f"[{time.strftime('%X')}] 開始任務: {message}")# asyncio.sleep 是一個異步的 time.sleep()# 當遇到 await asyncio.sleep() 時,事件循環會切換到其他任務await asyncio.sleep(delay)print(f"[{time.strftime('%X')}] 完成任務: {message}")# 定義主入口協程
async def main():print(f"[{time.strftime('%X')}] 程序開始")# 直接 await 調用協程await say_hello(2, "你好")await say_hello(1, "世界")print(f"[{time.strftime('%X')}] 程序結束")# 使用 asyncio.run() 啟動程序
if __name__ == "__main__":asyncio.run(main())

運行結果分析:

[13:30:00] 程序開始
[13:30:00] 開始任務: 你好
[13:30:02] 完成任務: 你好
[13:30:02] 開始任務: 世界
[13:30:03] 完成任務: 世界
[13:30:03] 程序結束

你會發現,這段代碼雖然是異步的,但執行順序和同步代碼一樣,總共耗時 3 秒。這是因為我們依次 await 了兩個協程,必須等第一個完成后,第二個才會開始。

那么,如何讓它們“同時”運行呢?

4. 并發執行:asyncio.gatherasyncio.create_task

為了真正實現并發,我們需要讓多個任務在事件循環中同時被調度。 [3]

asyncio.gather

asyncio.gather() 可以接收一個或多個可等待對象,將它們并發執行,并按輸入順序返回所有結果。 [9]

修改上面的 main 函數:

async def main():print(f"[{time.strftime('%X')}] 程序開始")# 使用 asyncio.gather 并發運行兩個協程await asyncio.gather(say_hello(2, "你好"),say_hello(1, "世界"))print(f"[{time.strftime('%X')}] 程序結束")# ... 其他代碼不變 ...

新的運行結果:

[13:32:10] 程序開始
[13:32:10] 開始任務: 你好
[13:32:10] 開始任務: 世界
[13:32:11] 完成任務: 世界
[13:32:12] 完成任務: 你好
[13:32:12] 程序結束

觀察時間戳,兩個任務幾乎是同時開始的。耗時1秒的任務先結束,耗時2秒的后結束。整個程序的總耗時取決于最長的那個任務,也就是 2 秒,而不是之前的 3 秒。這就是并發帶來的效率提升! [3]

asyncio.create_task

asyncio.create_task() 用于將一個協程包裝成一個任務 (Task),并提交給事件循環立即開始執行,而不需要馬上 await 它。 [6][7] TaskFuture 的一個子類,專門用于管理協程。 [4]

這就像是“發射后不管”(fire-and-forget),你創建了一個任務讓它在后臺運行,然后可以繼續做其他事情。 [6]

async def main():print(f"[{time.strftime('%X')}] 程序開始")# 創建任務,任務會立即開始在事件循環中被調度task1 = asyncio.create_task(say_hello(2, "你好"))task2 = asyncio.create_task(say_hello(1, "世界"))print(f"[{time.strftime('%X')}] 任務已創建")# 在這里可以做其他事情await asyncio.sleep(0.5)print(f"[{time.strftime('%X')}] 主程序做了一些其他工作")# 等待任務完成await task1await task2print(f"[{time.strftime('%X')}] 程序結束")

運行結果:

[13:35:20] 程序開始
[13:35:20] 任務已創建
[13:35:20] 開始任務: 你好
[13:35:20] 開始任務: 世界
[13:35:20] 主程序做了一些其他工作
[13:35:21] 完成任務: 世界
[13:35:22] 完成任務: 你好
[13:35:22] 程序結束

create_taskgather 的區別在于控制的粒度。gather 是一種更高級的抽象,適合一次性并發運行多個任務并收集結果的場景。 [9] create_task 則提供了更靈活的控制,允許你在任務運行期間執行其他邏輯。 [6][9]

5. 實戰演練:使用 aiohttp 并發下載網頁

理論講了這么多,讓我們來看一個最能體現 asyncio 價值的場景:并發網絡請求。我們將使用流行的異步 HTTP 客戶端庫 aiohttp。 [10][11]

首先,你需要安裝 aiohttp
pip install aiohttp

下面的例子將對比同步和異步方式獲取多個網頁標題所花費的時間。

import asyncio
import time
import aiohttp
import requests  # 用于同步對比urls = ['https://www.python.org','https://github.com','https://www.wikipedia.org','https://www.youtube.com','https://www.amazon.com',
]def get_title_sync(url):"""同步獲取網頁標題"""try:resp = requests.get(url, timeout=10)# 一個簡單的解析,實際應用中建議使用 BeautifulSoupreturn resp.text.split('<title>')[1].split('</title>')[0].strip()except Exception as e:return f"Error: {e}"async def get_title_async(session, url):"""異步獲取網頁標題"""try:# aiohttp 使用 session.get() 發起請求async with session.get(url, timeout=10) as resp:# resp.text() 是一個協程,需要 awaithtml = await resp.text()return html.split('<title>')[1].split('</title>')[0].strip()except Exception as e:return f"Error: {e}"async def main_async():# aiohttp 建議使用一個 ClientSession 來執行所有請求async with aiohttp.ClientSession() as session:tasks = [get_title_async(session, url) for url in urls]# 使用 gather 并發執行所有任務titles = await asyncio.gather(*tasks)for url, title in zip(urls, titles):print(f"{url}: {title}")if __name__ == "__main__":# --- 同步版本 ---print("--- 開始同步請求 ---")start_time_sync = time.time()for url in urls:title = get_title_sync(url)print(f"{url}: {title}")end_time_sync = time.time()print(f"同步請求總耗時: {end_time_sync - start_time_sync:.2f} 秒\n")# --- 異步版本 ---print("--- 開始異步請求 ---")start_time_async = time.time()asyncio.run(main_async())end_time_async = time.time()print(f"異步請求總耗時: {end_time_async - start_time_async:.2f} 秒")

典型的運行結果:

--- 開始同步請求 ---
https://www.python.org: Welcome to Python.org
https://github.com: GitHub: Let’s build from here
https://www.wikipedia.org: Wikipedia
https://www.youtube.com: YouTube
https://www.amazon.com: Amazon.com. Spend less. Smile more.
同步請求總耗時: 4.58 秒--- 開始異步請求 ---
https://www.python.org: Welcome to Python.org
https://github.com: GitHub: Let’s build from here
https://www.wikipedia.org: Wikipedia
https://www.youtube.com: YouTube
https://www.amazon.com: Amazon.com. Spend less. Smile more.
異步請求總耗時: 0.95 秒

結果一目了然。異步版本的速度比同步版本快了數倍。 [5] 這是因為 asyncio 在等待一個網站響應時,沒有閑著,而是立即去請求下一個網站,極大地利用了網絡 I/O 的等待時間。 [11]

6. 進階:協程間的同步與通信

當我們有多個協程并發運行時,有時它們需要訪問同一個資源,或者需要相互傳遞工作任務。這時,為了避免數據混亂和協調工作流程,就需要用到同步和通信機制。asyncio 提供了與多線程編程中類似的工具,但它們是為協程專門設計的。

6.1 資源保護:asyncio.Lock

在并發環境中,如果多個任務同時嘗試修改一個共享資源(例如一個變量或文件),就可能導致競爭條件 (Race Condition),使得最終結果不可預測。

雖然 asyncio 在單線程上運行,不會有真正的并行執行,但一個協程可以在 await 處被掛起,此時事件循環會運行另一個協程。如果這兩個協程都在修改同一個數據,問題依然存在。

asyncio.Lock 就是用來解決這個問題的。它保證在任何時候,只有一個協程能夠獲得鎖并執行“臨界區”代碼。

使用場景:保護對共享資源的訪問,確保操作的原子性。

讓我們看一個例子:多個協程同時增加一個共享計數器。

import asyncio# 一個共享的資源
shared_counter = 0async def unsafe_worker():"""一個沒有鎖保護的協程"""global shared_counter# 1. 讀取當前值current_value = shared_counter# 在這里,協程可能會被掛起,切換到另一個 workerawait asyncio.sleep(0.01) # 2. 基于舊值計算新值new_value = current_value + 1# 3. 寫入新值shared_counter = new_valueasync def safe_worker(lock):"""一個有鎖保護的協程"""global shared_counter# 使用 async with lock 語法可以自動獲取和釋放鎖async with lock:current_value = shared_counterawait asyncio.sleep(0.01)new_value = current_value + 1shared_counter = new_valueasync def main():global shared_counter# --- 演示不安全的情況 ---print("--- 演示不安全的情況 ---")shared_counter = 0tasks_unsafe = [unsafe_worker() for _ in range(100)]await asyncio.gather(*tasks_unsafe)print(f"沒有鎖保護,100個任務完成后的計數器值: {shared_counter}") # 結果通常遠小于100# --- 演示安全的情況 ---print("\n--- 演示安全的情況 ---")shared_counter = 0lock = asyncio.Lock()tasks_safe = [safe_worker(lock) for _ in range(100)]await asyncio.gather(*tasks_safe)print(f"使用鎖保護,100個任務完成后的計數器值: {shared_counter}") # 結果總是100if __name__ == "__main__":asyncio.run(main())

代碼解讀與結果分析:

  • unsafe_worker: 在讀取 (current_value = ...) 和寫入 (shared_counter = ...) 之間有一個 await。這給了事件循環切換到另一個 unsafe_worker 的機會。多個 worker 可能會基于同一個舊值進行計算,導致一些增加操作丟失。因此,最終結果會小于 100。
  • safe_worker: 使用了 async with lock:。當一個協程進入這個代碼塊時,它會獲取鎖。如果此時其他協程也想進入,它們必須 await,直到第一個協程執行完畢并自動釋放鎖。這確保了“讀-改-寫”這個操作的完整性,所以最終結果總是正確的 100。

6.2 任務分發:asyncio.Queue

asyncio.Queue 是一個為異步編程設計的隊列,它非常適合經典的生產者-消費者 (Producer-Consumer) 模型。

  • 生產者 (Producer):創建任務或數據,并將其放入隊列。
  • 消費者 (Consumer):從隊列中取出任務或數據,并進行處理。

隊列本身處理了所有的同步邏輯:

  • 如果消費者試圖從空隊列中獲取 (get) 數據,它會自動 await,直到隊列中有新數據。
  • 如果生產者試圖向一個已滿的隊列(如果創建時指定了 maxsize)中放入 (put) 數據,它會自動 await,直到隊列有空位。

使用場景:解耦任務的創建和執行,實現任務分發系統,控制并發處理任務的數量。

讓我們構建一個簡單的爬蟲模型:一個生產者負責發現 URL 并放入隊列,多個消費者負責從隊列中取出 URL 并“下載”。

import asyncio
import randomasync def producer(queue, num_urls):"""生產者:生成一些模擬的URL并放入隊列"""print("生產者啟動...")for i in range(num_urls):url = f"https://example.com/page/{i}"# 模擬發現URL需要一些時間await asyncio.sleep(random.uniform(0.1, 0.5))# 將URL放入隊列await queue.put(url)print(f"生產者放入: {url}")print("生產者完成任務。")async def consumer(name, queue):"""消費者:從隊列中獲取URL并處理"""print(f"消費者 {name} 啟動...")# 持續從隊列中獲取任務while True:# 從隊列中獲取URL,如果隊列為空,會在此處等待url = await queue.get()print(f"消費者 {name} 正在處理: {url}")# 模擬處理任務需要的時間await asyncio.sleep(random.uniform(0.5, 1.5))print(f"消費者 {name} 完成處理: {url}")# 必須調用 task_done() 來通知隊列這個任務已經處理完畢queue.task_done()async def main():# 創建一個不限大小的隊列task_queue = asyncio.Queue()num_urls_to_produce = 10num_consumers = 3# 啟動生產者producer_task = asyncio.create_task(producer(task_queue, num_urls_to_produce))# 啟動多個消費者consumer_tasks = []for i in range(num_consumers):task = asyncio.create_task(consumer(f"C{i+1}", task_queue))consumer_tasks.append(task)# 等待生產者完成所有URL的放入await producer_taskprint("所有URL已放入隊列,等待消費者處理...")# 等待隊列中的所有任務都被處理完畢# queue.join() 會阻塞,直到隊列中每個項目的 task_done() 都被調用await task_queue.join()print("所有任務處理完畢!")# 所有任務都處理完了,消費者們還在 while True 循環里等待新任務# 為了讓程序能正常退出,我們需要取消這些消費者任務for task in consumer_tasks:task.cancel()if __name__ == "__main__":asyncio.run(main())

代碼解讀與關鍵點:

  1. queue.put(item): 生產者使用它來異步地添加項目。
  2. queue.get(): 消費者使用它來異步地獲取項目。這是主要的同步點。
  3. queue.task_done(): 這是至關重要的一步!消費者處理完一個項目后,必須調用此方法。它會減少隊列的內部計數器。
  4. queue.join(): main 函數用它來等待所有項目都被處理。它會一直阻塞,直到隊列的內部計數器歸零。這確保了我們在程序結束前,所有工作都已完成。
  5. 任務取消: 因為消費者通常在一個無限循環中工作,當所有工作完成后,我們需要顯式地取消它們,否則 asyncio.run(main()) 將永遠不會退出。

7. 總結

asyncio 為 Python 帶來了強大的并發能力,是構建高性能網絡應用和服務的利器。

核心要點回顧:

  • 適用場景:I/O 密集型任務(如網絡爬蟲、Web 服務器、數據庫連接等)。
  • 核心語法async def 定義協程,await 暫停協程并等待結果。
  • 啟動方式asyncio.run() 是現代 Python 中啟動異步程序的標準方式。
  • 并發執行:使用 asyncio.gather()asyncio.create_task() 來并發運行多個任務。
  • 同步與通信:使用 asyncio.Lock 保護共享資源,避免競爭條件;使用 asyncio.Queue 構建生產者-消費者模型,高效地分發和處理任務。
  • 生態系統:需要配合 aiohttp, aiodns, asyncpg 等異步庫才能發揮最大威力。

從 Python 3.4 首次引入 asyncio 至今,它已經變得越來越成熟和易用。雖然異步編程的思維方式需要一些時間來適應,但一旦你掌握了它,它將成為你工具箱中應對高并發挑戰的一把“瑞士軍刀”。希望這篇博客能為你打開異步編程的大門。


參考文章

  1. asyncio 教程- 什么是異步? - Graia 官方文檔
  2. Python asyncio 模塊 - 菜鳥教程
  3. Asyncio in Python: A Comprehensive Guide with Examples. | by Obafemi - Medium
  4. 使用asyncio - python并發編程-中文版
  5. Python asyncio 從不會到上路 - MyApollo
  6. Solve Common Asynchronous Scenarios With Python’s “asyncio” - Better Programming
  7. Coroutines and Tasks — Python 3.13.5 documentation
  8. 使用asyncio - Python教程- 廖雪峰的官方網站
  9. Is it more efficient to use create_task(), or gather()? - Stack Overflow
  10. python asyncio 異步I/O - 實現并發http請求(asyncio + aiohttp) - yuminhu - 博客園
  11. python asyncio 異步I/O - 實現并發http請求(asyncio + aiohttp) - 上海-悠悠- 博客園
  12. asyncio教程原創 - CSDN博客

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

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

相關文章

OpenCV顏色矩哈希算法------cv::img_hash::ColorMomentHash

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 該類實現了顏色矩哈希算法&#xff08;Color Moment Hash&#xff09;&#xff0c;用于圖像相似性比較。它基于圖像在HSV顏色空間中的顏色矩統計特…

上海交大醫學院張維拓老師赴同濟醫院做R語言訓練營培訓

當前&#xff0c;醫學與人工智能的深度融合正迎來歷史性發展機遇。華中科技大學同濟醫學院附屬同濟醫院&#xff08;以下簡稱“同濟醫院”&#xff09;作為醫療人工智能應用的先行探索者&#xff0c;已在電子病歷輔助書寫、科研數據分析、醫療合同自動化審核等關鍵場景完成試點…

使用阿里云/騰訊云安裝完成mysql使用不了

顯示錯誤1130 - Host 106.228.110.117 is not allowed to connect to this MySQL server解決方案進入服務器的mysql命令行mysql -u root -p查看數據庫SHOW DATABASES;選擇mysql數據庫USE mysql;查看里面的表SHOW TABLES;查詢user表格的權限限制SELECT Host, User FROM user;將權…

第35周—————糖尿病預測模型優化探索

目錄 目錄 前言 1.檢查GPU 2.查看數據 ?編輯 3.劃分數據集 4.創建模型與編譯訓練 5.編譯及訓練模型 6.結果可視化 7.總結 前言 &#x1f368; 本文為&#x1f517;365天深度學習訓練營中的學習記錄博客 &#x1f356; 原作者&#xff1a;K同學啊 1.檢查GPU import torch.n…

接口(上篇)

接口&#xff08;上篇&#xff09;1.概念2.語法規則3.使用和特性4.實現多接口5.接口間繼承1.概念 接口就是公共的行為規范標準&#xff0c;大家在實現時&#xff0c; 只要符合規范標準&#xff0c;就可以通用。 在Java中&#xff0c;接口可以看成是&#xff1a;多個類的公共規…

UE5 源碼編譯setup.bat報錯

文章目錄編譯報錯改動說明小結更新編譯報錯 改動說明 因為需要整服務器&#xff0c;就編譯源碼&#xff0c;然后就遇到這個&#xff0c;很無語。這個問題一直存在&#xff0c;UE官方也不修復&#xff0c;也算是修復了&#xff0c;只是每次都要去重新下載替換下。也可以去問問d…

Linux下PCIe子系統(二)——PCIe子系統框架詳解

Linux下PCIe子系統&#xff08;二&#xff09;——PCIe子系統框架詳解 1. 概述 PCIe&#xff08;PCI Express&#xff09;子系統是Linux內核中負責管理PCI/PCIe設備的核心組件。它提供了一套完整的框架來發現、配置和管理PCI設備&#xff0c;實現了設備的即插即用和熱插拔功能。…

[特殊字符] LLM(大型語言模型):智能時代的語言引擎與通用推理基座

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 從千億參數到人類認知的AI革命 &#x1f50d; 一、核心定義與核心特征…

18-C#改變形參內容

C#改變形參內容 1.ref 參數 int A100; add1(ref A) public int add1 (ref int x) {x x 10;return x; }2.out 參數 int A100; int B200; int Z; add3(A,B, out Z) public int add3 (int x&#xff0c;int y&#xff0c;int z) {z x y;return z; }

恒盾C#混淆加密大師最新版本1.4.0更新 - 增強各類加密效果, 提升兼容性, 使.NET加密更簡單

C#/.NET作為托管語言, 其編譯生成的EXE/DLL極易被反編譯工具還原源碼。據統計&#xff0c;大量的商業軟件曾遭遇過代碼逆向風險&#xff0c;導致核心算法泄露、授權被跳過. 因此對于C#語言開發的.NET程序來說, 在發布前進行混淆和加密非常有必要. 恒盾C#混淆加密大師作為一款.N…

數學建模:非線性規劃:二次規劃問題

一、定義如果規劃模型的目標函數是決策向量的二次函數&#xff0c;約束條件都是線性的&#xff0c;那么這個模型稱為二次規劃&#xff08;QP&#xff09;模型。二次規劃模型的一般形式為二、性質凸性判定準則二次規劃問題的凸性完全由Hessian矩陣H決定&#xff1a;??嚴格凸QP…

4. 那在詳細說一下 http 2.0 的特點

總結 二進制協議&#xff1a;文本通信改為二進制幀通信&#xff0c;數據可以劃分為更小的幀&#xff0c;便于高效解析和傳輸。多路復用&#xff1a;廢除 pipeline 管道&#xff0c;避免了“隊頭阻塞”問題。允許同一個 TCP 連接同時發送多個請求和協議&#xff0c;提高網絡資源…

Qt中遍歷QMap的多種方法及性能分析

Qt中遍歷QMap的多種方法及性能分析遍歷QMap的方法**1、使用迭代器&#xff08;STL風格&#xff09;****2、使用Java風格迭代器****3、使用C11范圍循環****4、使用鍵值分離遍歷**性能分析使用建議遍歷QMap的方法 1、使用迭代器&#xff08;STL風格&#xff09; QMap<QStrin…

Unity3D物理引擎性能優化策略

前言 在Unity3D中優化物理引擎性能&#xff0c;尤其是處理3D碰撞器與2D碰撞器的映射問題&#xff0c;需要結合系統特性和最佳實踐。以下是關鍵策略和實現方案&#xff1a; 對惹&#xff0c;這里有一個游戲開發交流小組&#xff0c;希望大家可以點擊進來一起交流一下開發經驗呀…

集群與集群應用

負載均衡與高可用綜合實驗一、集群是什么&#xff1f;是有一組獨立的計算機系統構成的一個松耦合的多處理系統&#xff0c;作為一個整體向用戶提供一組網絡資源&#xff0c;這些單個的計算機就是集群的節點。二、集群類型Load Balance cluster&#xff08;負載均衡集群&#xf…

jmm,`as - if - serial` 與 `happens - before` 原則

在Java并發編程中&#xff0c;as - if - serial 與 happens - before 原則是確保程序在多線程環境下正確執行的重要規則&#xff0c;下面為你詳細講解&#xff1a; as - if - serial原則 定義&#xff1a;as - if - serial 原則是指&#xff0c;不管編譯器和處理器如何優化&…

主流大模型Agent框架 AutoGPT詳解

注&#xff1a;此文章內容均節選自充電了么創始人&#xff0c;CEO兼CTO陳敬雷老師的新書《GPT多模態大模型與AI Agent智能體》&#xff08;跟我一起學人工智能&#xff09;【陳敬雷編著】【清華大學出版社】 GPT多模態大模型與AI Agent智能體書籍本章配套視頻課程【陳敬雷】 文…

kotlin學習,val使用get()的問題

疑問&#xff1a;定義val怎么還能使用get()代碼示例&#xff1a;private val nametype:Intget()Business.carInfo?.let{carSc(it)}?:LType.AS回答&#xff1a;Kotlin 允許為屬性定義自定義 getter&#xff0c;每次訪問屬性時會執行該方法疑問&#xff1a;這里引出另一個不解&…

解決el-select數據類型相同但是顯示數字的問題

這個不是我寫的&#xff0c;只是遇到的bug&#xff0c;寫法問題&#xff0c;忽略了值的綁定的問題源代碼bug&#xff1a;<el-selectv-model"schemeInfo.horizon"placeholder"請選擇起報月份"clearablefilterable><el-option v-for"(option,i…

熟練掌握RabbitMQ和Kafka的使用及相關應用場景。異步通知與解耦,流量削峰,配合本地消息表實現事務的最終一致性并解決消息可靠、順序消費和錯誤重試等問題

RabbitMQstock.#.nyse &#xff0c;#匹配多個字符&#xff0c;*匹配一個字符。 Confirm Callback 到達exchange的回調。 Return Callback 到達queue失敗的回調。 Kafka Kafka生產端分區器&#xff1a; 1.直接指定partition 指定0,1。 2.設置hashkey&#xff0c;計算key的hash值…