pythonasyncio和await,python asyncio原理

本文目錄一覽:

進程,線程,協程,同步,並發,非同步

1,我們公司一開始帶飯的人不是很多,公司只有一個茶水間,於是行政的小姐姐很體貼的買了一個微波爐放到茶水間,然後大家中午可以在茶水間排隊去熱飯。很開心。

2,後來公司發展越來越好,不斷有新鮮的血液注入到公司,充滿了活力,帶飯的人越來越多,行政小姐姐發現一個微波爐根本不夠用,於是又賣了兩個,大家中午繼續去茶水間排隊去熱飯,速度比以前快了許多,大大緩解了排隊過長的壓力。

3,某一天公司上市了,發展一片大好,人員也越來越多,於是公司換了新的辦公地點,將原來的一個茶水間擴充到了3個,微波爐每個茶水間放5個,同時雇了一個阿姨,專門輔助員工的一些生活方面的事,讓員工可以安安心心工作,於是變成了這樣,到中午的時候,員工選擇3個茶水間一個,將飯盒放在茶水間里,阿姨負責將飯熱好放到指定位置,一個阿姨就可以同時操作多個微波爐,一個熱好後取出放入下一個,大大提高了熱飯效率,熱好的飯可以放到指定位置一會自己來取即可,也可以送到員工的位置,大家中午再也不用排隊熱飯了,

多個茶水間相當於多進程(放在python也可以理解為多核),大大提高了效率,但同時開銷也很多,增加一個茶水間的代價遠大於增加一個微波爐。

進程,直觀點說,保存在硬碟上的程序運行以後,會在內存空間里形成一個獨立的內存體,這個內存體 有自己獨立的地址空間,有自己的堆 ,上級掛靠單位是操作系統。 操作系統會以進程為單位,分配系統資源(CPU時間片、內存等資源),進程是資源分配的最小單位 。

大家排隊去茶水間熱飯,先到的先熱,同一茶水間同時只有一個人在熱飯。即使有多個微波爐也是順序的開始工作,三個微波爐相當於三個線程,同時可以熱三份飯,但熱飯的人是順序進入茶水間的。

線程,有時被稱為輕量級進程(Lightweight Process,LWP),是操作系統調度(CPU調度)執行的最小單位 。

阿姨可以同時操作多個微波爐,一個熱好後取出放入下一個,大大提高了熱飯效率,哪個微波爐熱好就先用哪個,所以協程是無序的,大大提高了工作效率。

一個阿姨在多個茶水間和微波爐工作 充分利用多核

協程,是一種比線程更加輕量級的存在,協程不是被操作系統內核所管理,而完全是由程序所控制(也就是在用戶態執行)。這樣帶來的好處就是性能得到了很大的提升,不會像線程切換那樣消耗資源。

協程在子程序內部是可中斷的,然後轉而執行別的子程序,在適當的時候再返回來接著執行 。

阿姨熱好飯之後放到指定位置,每個人可以根據自己的時間過來取,如果阿姨空閑了也可以送到員工工位。

公司初期人員較少,所有員工在唯一的一個茶水間排隊使用同一個微波爐,順序使用微波爐。

公司中期增加了微波爐的數量,所有員工在茶水間排隊使用多個微波爐,多個微波爐同時工作。

公司發展後期增加了茶水間和微波爐的數量,只有一個阿姨使用多個茶水間的微波爐,只有在微波爐可以使用的條件下才去使用,其他時間可以干其他事情。

【區別】:

調度 : 線程作為調度和分配的基本單位,進程作為擁有資源的基本單位 ;

並發性 : 不僅進程之間可以並發執行,同一個進程的多個線程之間也可並發執行 ;

擁有資源 : 進程是擁有資源的一個獨立單位,線程不擁有系統資源 ,但可以訪問隸屬於進程的資源。進程所維護的是程序所包含的資源(靜態資源), 如: 地址空間,打開的文件句柄集,文件系統狀態,信號處理handler等 ;線程所維護的運行相關的資源(動態資源),如: 運行棧,調度相關的控制信息,待處理的信號集等 ;

系統開銷 :在創建或撤消進程時,由於系統都要為之分配和回收資源,導致系統的開銷明顯大於創建或撤消線程時的開銷。但是進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變數,但線程之間沒有單獨的地址空間,一個進程死掉就等於所有的線程死掉,所以 多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些 。

【聯繫】:

一個線程只能屬於一個進程,而一個進程可以有多個線程,但至少有一個線程 ;

資源分配給進程,同一進程的所有線程共享該進程的所有資源;

處理機分給線程,即 真正在處理機上運行的是線程 ;

線程在執行過程中,需要協作同步。不同進程的線程間要利用消息通信的辦法實現同步。

協程的特點在於是一個線程執行,那和多線程比,協程有何 優勢 ?

極高的執行效率 :因為 子程序切換不是線程切換,而是由程序自身控制 ,因此, 沒有線程切換的開銷 ,和多線程比,線程數量越多,協程的性能優勢就越明顯;

不需要多線程的鎖機制 :因為只有一個線程,也不存在同時寫變數衝突, 在協程中控制共享資源不加鎖 ,只需要判斷狀態就好了,所以執行效率比多線程高很多。

(1)進程可使用multiprocessing包實現

與threading.Thread類似,它可以利用multiprocessing.Process對象來創建一個進程。

該進程可以運行在Python程序內部編寫的函數。

該Process對象與Thread對象的用法相同,也有start(), run(), join()的方法。

(2)線程可使用threading包或thread包

(3)協程通過async await 實現,async聲明一個函數為非同步函數,await可將程序掛起,去執行其他的非同步程序。協程 有兩種,一種 無棧協程,python 中 以 asyncio 為代表, 一種有棧協程,python 中 以 gevent 為代表。

(1)以多進程形式,允許多個任務同時運行;

(2)以多線程形式,允許單個任務分成不同的部分運行;

(3)提供協調機制,一方面防止進程之間和線程之間產生衝突,另一方面允許進程之間和線程之間共享資源。

python里怎麼實現多個協程一起執行,只要完成

import asyncio

 

 

async def phase(i):

    print(‘in phase {}’.format(i))

    await asyncio.sleep(0.5 – (0.1 * i))

    print(‘done with phase {}’.format(i))

    return ‘phase {} result’.format(i)

 

 

async def main(num_phases):

    print(‘starting main’)

    phases = [

        phase(i)

        for i in range(num_phases)

    ]

    print(‘waiting for phases to complete’)

    results = []

    for next_to_complete in asyncio.as_completed(phases):

        answer = await next_to_complete

        print(‘received answer {!r}’.format(answer))

        results.append(answer)

    print(‘results: {!r}’.format(results))

    return results

 

 

event_loop = asyncio.get_event_loop()

try:

    event_loop.run_until_complete(main(3))

finally:

    event_loop.close()

在Python中使用Asyncio系統(3-4)Task 和 Future

Task 和 Future

前面我們討論了協程,以及如何在循環中運行它們才有用。現在我想簡單談談Task和Future api。你將使用最多的是Task,因為你的大部分工作將涉及使用create_task()函數運行協程,就像在第22頁的「快速開始」中設置的那樣。Future類實際上是Task的超類,它提供了與循環交互操作的所有功能。

可以這樣簡單地理解:Future表示某個活動的未來完成狀態,並由循環管理。Task是完全相同的,但是具體的「activity」是一個協程——可能是你用async def函數加上create_task()創建的協程。

Future類表示與循環交互的某個東西的狀態。這個描述太模糊了,不太有用,所以你可以將Future實例視為一個切換器,一個完成狀態的切換器。當創建Future實例時,切換設置為「尚未完成」狀態,但稍後它將是「完成」狀態。事實上,Future實例有一個名為done()的方法,它允許你檢查狀態,如示例 3-15所示。

示例 3-15. 用done()方法檢查完成狀態

Future實例還可以執行以下操作:

• 設置一個result值(用.set_result(value)設置值並且使用 .result()獲取值)

• 使用.cancel()方法取消 (並且會用使用.cancelled()檢查是否取消)

• 增加一個Future完成時回調的函數

即使Task更常見,也不可能完全避免使用Future:例如,在執行器上運行函數將返回Future實例,而不是Task。讓我們快速看一下 示例 3-16 ,了解一下直接使用Future實例是什麼感覺。

示例 3-16. 與Future實例的交互

(L3)創建一個簡單的 main函數。我們運行這個函數,等上一會兒然後在Future f上設置一個結果。

(L5)設置一個結果。

(L8)手動創建一個Future實例。注意,這個實例(默認情況下)綁定到我們的循環,但它沒有也不會被附加到任何協程(這就是Tasks的作用)。

(L9)在做任何事情之前,確認future還沒有完成。

(L11)安排main()協程,傳遞future。請記住,main()協程所做的所有工作就是sleep,然後切換Future實例。(注意main()協程還不會開始運行:協程只在事件循環運行時才開始運行。)

(L13)在這裡我們在Future實例上而不是Task實例上使用run_until_complete()。這和你以前見過的不一樣。現在循環正在運行,main()協程將開始執行.

(L16)最終,當future的結果被設置時,它就完成了。完成後,可以訪問結果。

當然,你不太可能以這裡所示的方式直接使用Future;代碼示例僅用於教育目的。你與asynccio的大部分聯繫都是通過Task實例進行的。

你可能想知道如果在Task實例上調用set_result()會發生什麼。在Python 3.8之前可以這樣做,但現在不允許這麼做了。任務實例是協程對象的包裝器,它們的結果值只能在內部設置為底層協程函數的結果,如 示例 3-17所示那樣。

示例 3-17. 在task上調用set_result

(L13)唯一的區別是我們創建的是Task實例而不是Future實例。當然,Task API要求我們提供一個協程;這裡我們使用sleep()只是因為簡單方便。

(L7)正在傳入一個Task實例。它滿足函數的類型簽名(因為Task是Future的子類),但從Python 3.8開始,我們不再允許在Task上調用set_result():嘗試這樣做將引發RuntimeError。這個想法是,一個Task代表一個正在運行的協程,所以結果應該總是來自於task自身。

(L10, L24)但是,我們仍然可以cancel()一個任務,它將在底層協程中引發CancelledError。

Create_task? Ensure_Future? 下定決心吧!

在第22頁的「快速入門」中,我說過運行協程的方法是使用asyncio.create_task()。在引入該函數之前,有必要獲取一個循環實例並使用loop.create_task()完成相同的任務。事實上,這也可以通過一個不同的模塊級函數來實現:asyncio.ensure_future()。一些開發人員推薦create_task(),而其他人推薦ensure_future()。

在我為這本書做研究的過程中,我確信API方法asyncio.ensure_future()是引起對asyncio庫廣泛誤解的罪魁禍首。API的大部分內容都非常清晰,但在學習過程中還存在一些嚴重的障礙,這就是其中之一。當你遇到ensure_future()時,你的大腦會非常努力地將其集成到關於asyncio應該如何使用的心理模型中——但很可能會失敗!

在Python 3.6 asyncio 文檔中,這個現在已經臭名昭著的解釋突出了 ensure_future() 的問題:

asyncio.ensure_future(coro_or_future, *, _loop =None)

安排執行一個協程對象:把它包裝在future中。返回一個Task對象。如果參數是Future,則直接返回。

什麼!? 當我第一次讀到這篇文章時,我很困惑。下面希望是對ensure_future()的更清楚的描述:

這個函數很好地說明了針對終端用戶開發人員的asyncio API(高級API)和針對框架設計人員的asyncio API(低級API)之間的區別。讓我們在示例 3-18中自習看看它是如何工作的。

示例 3-18. 仔細看看ensure_future()在做什麼

(L3)一個簡單的什麼都不做的協程函數。我們只需要一些能組成協程的東西。

(L6)我們通過直接調用該函數來創建協程對象。你的代碼很少會這樣做,但我想在這裡明確地表示,我們正在向每個create_task()和ensure_future()傳遞一個協程對象。

(L7)獲取一個循環。

(L9)首先,我們使用loop.create_task()在循環中調度協程,並返回一個新的Task實例。

(L10)驗證類型。到目前為止,沒有什麼有趣的。

(L12)我們展示了asyncio.ensure_future()可以被用來執行與create_task()相同的動作:我們傳入了一個協程,並返回了一個Task實例(並且協程已經被安排在循環中運行)!如果傳入的是協程,那麼loop.create_task()和asyncio.ensure_future()之間沒有區別。

(L15)如果我們給ensure_future()傳遞一個Task實例會發生什麼呢?注意我們要傳遞的Task實例是已經在第4步通過loop.create_task()創建好的。

(L16)返回的Task實例與傳入的Task實例完全相同:它在被傳遞時沒有被改變。

直接傳遞Future實例的意義何在?為什麼用同一個函數做兩件不同的事情?答案是,ensure_future()的目的是讓框架作者向最終用戶開發者提供可以處理兩種參數的API。不相信我?這是ex-BDFL自己說的:

ensure_future()的要點是,如果你有一個可能是協程或Future(後者包括一個Task,因為它是Future的子類)的東西,並且你想能夠調用一個只在Future上定義的方法(可能唯一有用的例子是cancel())。當它已經是Future(或Task)時,它什麼也不做;當它是協程時,它將它包裝在Task中。

如果您知道您有一個協程,並且希望它被調度,那麼正確的API是create_task()。唯一應該調用ensure_future()的時候是當你提供一個API(像大多數asyncio自己的API),它接受協程或Future,你需要對它做一些事情,需要你有一個Future。

—Guido van Rossum

總而言之,asyncio.sure_future()是一個為框架設計者準備的輔助函數。這一點最容易通過與一種更常見的函數進行類比來解釋,所以我們來做這個解釋。如果你有幾年的編程經驗,你可能已經見過類似於例3-19中的istify()函數的函數。示例 3-19中listify()的函數。

示例 3-19. 一個強制輸入列表的工具函數

這個函數試圖將參數轉換為一個列表,不管輸入的是什麼。api和框架中經常使用這類函數將輸入強制轉換為已知類型,這將簡化後續代碼——在本例中,您知道參數(來自listify()的輸出)將始終是一個列表。

如果我將listify()函數重命名為ensure_list(),那麼您應該開始看到與asyncio.ensure_future()的類似之處:它總是試圖將參數強制轉換為Future(或子類)類型。這是一個實用函數,它使框架開發人員(而不是像你我這樣的終端用戶開發人員)的工作變得更容易。

實際上,asyncio標準庫模塊本身使用ensure_future()正是出於這個原因。當你下次查看API時,你會發現函數參數被描述為「可等待對象」,很可能內部使用ensure_future()強制轉換參數。例如,asyncio.gather()函數就像下面的代碼一樣:

aws參數表示「可等待對象」,包括協程、task和future。在內部,gather()使用ensure_future()進行類型強制轉換:task和future保持不變,而把協程強制轉為task。

這裡的關鍵是,作為終端用戶應用程序開發人員,應該永遠不需要使用asyncio.ensure_future()。它更像是框架設計師的工具。如果你需要在事件循環上調度協程,只需直接使用asyncio.create_task()來完成。

在接下來的幾節中,我們將回到語言級別的特性,從非同步上下文管理器開始。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/179952.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-22 05:09
下一篇 2024-11-22 05:09

相關推薦

  • Harris角點檢測演算法原理與實現

    本文將從多個方面對Harris角點檢測演算法進行詳細的闡述,包括演算法原理、實現步驟、代碼實現等。 一、Harris角點檢測演算法原理 Harris角點檢測演算法是一種經典的計算機視覺演算法…

    編程 2025-04-29
  • 瘦臉演算法 Python 原理與實現

    本文將從多個方面詳細闡述瘦臉演算法 Python 實現的原理和方法,包括該演算法的意義、流程、代碼實現、優化等內容。 一、演算法意義 隨著科技的發展,瘦臉演算法已經成為了人們修圖中不可缺少…

    編程 2025-04-29
  • 神經網路BP演算法原理

    本文將從多個方面對神經網路BP演算法原理進行詳細闡述,並給出完整的代碼示例。 一、BP演算法簡介 BP演算法是一種常用的神經網路訓練演算法,其全稱為反向傳播演算法。BP演算法的基本思想是通過正…

    編程 2025-04-29
  • GloVe詞向量:從原理到應用

    本文將從多個方面對GloVe詞向量進行詳細的闡述,包括其原理、優缺點、應用以及代碼實現。如果你對詞向量感興趣,那麼這篇文章將會是一次很好的學習體驗。 一、原理 GloVe(Glob…

    編程 2025-04-27
  • 編譯原理語法分析思維導圖

    本文將從以下幾個方面詳細闡述編譯原理語法分析思維導圖: 一、語法分析介紹 1.1 語法分析的定義 語法分析是編譯器中將輸入的字元流轉換成抽象語法樹的一個過程。該過程的目的是確保輸入…

    編程 2025-04-27
  • Python字典底層原理用法介紹

    本文將以Python字典底層原理為中心,從多個方面詳細闡述。字典是Python語言的重要組成部分,具有非常強大的功能,掌握其底層原理對於學習和使用Python將是非常有幫助的。 一…

    編程 2025-04-25
  • Grep 精準匹配:探究匹配原理和常見應用

    一、什麼是 Grep 精準匹配 Grep 是一款在 Linux 系統下常用的文本搜索和處理工具,精準匹配是它最常用的一個功能。Grep 精準匹配是指在一個文本文件中查找與指定模式完…

    編程 2025-04-25
  • 深入探討馮諾依曼原理

    一、原理概述 馮諾依曼原理,又稱「存儲程序控制原理」,是指計算機的程序和數據都存儲在同一個存儲器中,並且通過一個統一的匯流排來傳輸數據。這個原理的提出,是計算機科學發展中的重大進展,…

    編程 2025-04-25
  • 樸素貝葉斯原理詳解

    一、樸素貝葉斯基礎 樸素貝葉斯是一種基於貝葉斯定理的演算法,用於分類和預測。貝葉斯定理是一種計算條件概率的方法,即已知某些條件下,某事件發生的概率,求某條件下另一事件發生的概率。樸素…

    編程 2025-04-25
  • 單點登錄原理

    一、什麼是單點登錄 單點登錄(Single Sign On,SSO)指的是用戶只需要登錄一次,在多個應用系統中使用同一個賬號和密碼登錄,而且在所有系統中都可以使用,而不需要在每個系…

    編程 2025-04-25

發表回復

登錄後才能評論