異步是計(jì)算機(jī)多線程的異步處理。與同步處理相對(duì),異步處理不用阻塞當(dāng)前線程來等待處理完成,而是允許后續(xù)操作,直至其它線程將處理完成,并回調(diào)通知此線程。

創(chuàng)新互聯(lián)云計(jì)算的互聯(lián)網(wǎng)服務(wù)提供商,擁有超過13年的服務(wù)器租用、成都托管服務(wù)器、云服務(wù)器、雅安服務(wù)器托管、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗(yàn),已先后獲得國家工業(yè)和信息化部頒發(fā)的互聯(lián)網(wǎng)數(shù)據(jù)中心業(yè)務(wù)許可證。專業(yè)提供云主機(jī)、雅安服務(wù)器托管、主機(jī)域名、VPS主機(jī)、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。
協(xié)程中更底層的對(duì)象:future對(duì)象,一般不會(huì)直接使用,它是task類的基類,task一部分的功能是future提供的,其是更低級(jí)的接口,幫助我們?nèi)サ却惒降慕Y(jié)果。例如res=await task1,等結(jié)果是由furture對(duì)象來創(chuàng)建的,furture對(duì)象內(nèi)部封裝了一個(gè)_state的值,維護(hù)著狀態(tài),如果變成finish完成,就不再等待,而是往后執(zhí)行、
task對(duì)象繼承future,task對(duì)象內(nèi)部await結(jié)果的處理是基于Future對(duì)象進(jìn)行處理的。
示例1:如果沒有任何結(jié)果的任務(wù),程序會(huì)一直運(yùn)行。
示例2:手動(dòng)給future對(duì)象賦值。一般不會(huì)手動(dòng)去設(shè)置這個(gè)。僅了解。
Task 和 Future
前面我們討論了協(xié)程,以及如何在循環(huán)中運(yùn)行它們才有用。現(xiàn)在我想簡單談?wù)凾ask和Future api。你將使用最多的是Task,因?yàn)槟愕拇蟛糠止ぷ鲗⑸婕笆褂胏reate_task()函數(shù)運(yùn)行協(xié)程,就像在第22頁的“快速開始”中設(shè)置的那樣。Future類實(shí)際上是Task的超類,它提供了與循環(huán)交互操作的所有功能。
可以這樣簡單地理解:Future表示某個(gè)活動(dòng)的未來完成狀態(tài),并由循環(huán)管理。Task是完全相同的,但是具體的“activity”是一個(gè)協(xié)程——可能是你用async def函數(shù)加上create_task()創(chuàng)建的協(xié)程。
Future類表示與循環(huán)交互的某個(gè)東西的狀態(tài)。這個(gè)描述太模糊了,不太有用,所以你可以將Future實(shí)例視為一個(gè)切換器,一個(gè)完成狀態(tài)的切換器。當(dāng)創(chuàng)建Future實(shí)例時(shí),切換設(shè)置為“尚未完成”狀態(tài),但稍后它將是“完成”狀態(tài)。事實(shí)上,F(xiàn)uture實(shí)例有一個(gè)名為done()的方法,它允許你檢查狀態(tài),如示例 3-15所示。
示例 3-15. 用done()方法檢查完成狀態(tài)
Future實(shí)例還可以執(zhí)行以下操作:
? 設(shè)置一個(gè)result值(用.set_result(value)設(shè)置值并且使用 .result()獲取值)
? 使用.cancel()方法取消 (并且會(huì)用使用.cancelled()檢查是否取消)
? 增加一個(gè)Future完成時(shí)回調(diào)的函數(shù)
即使Task更常見,也不可能完全避免使用Future:例如,在執(zhí)行器上運(yùn)行函數(shù)將返回Future實(shí)例,而不是Task。讓我們快速看一下 示例 3-16 ,了解一下直接使用Future實(shí)例是什么感覺。
示例 3-16. 與Future實(shí)例的交互
(L3)創(chuàng)建一個(gè)簡單的 main函數(shù)。我們運(yùn)行這個(gè)函數(shù),等上一會(huì)兒然后在Future f上設(shè)置一個(gè)結(jié)果。
(L5)設(shè)置一個(gè)結(jié)果。
(L8)手動(dòng)創(chuàng)建一個(gè)Future實(shí)例。注意,這個(gè)實(shí)例(默認(rèn)情況下)綁定到我們的循環(huán),但它沒有也不會(huì)被附加到任何協(xié)程(這就是Tasks的作用)。
(L9)在做任何事情之前,確認(rèn)future還沒有完成。
(L11)安排main()協(xié)程,傳遞future。請(qǐng)記住,main()協(xié)程所做的所有工作就是sleep,然后切換Future實(shí)例。(注意main()協(xié)程還不會(huì)開始運(yùn)行:協(xié)程只在事件循環(huán)運(yùn)行時(shí)才開始運(yùn)行。)
(L13)在這里我們?cè)贔uture實(shí)例上而不是Task實(shí)例上使用run_until_complete()。這和你以前見過的不一樣。現(xiàn)在循環(huán)正在運(yùn)行,main()協(xié)程將開始執(zhí)行.
(L16)最終,當(dāng)future的結(jié)果被設(shè)置時(shí),它就完成了。完成后,可以訪問結(jié)果。
當(dāng)然,你不太可能以這里所示的方式直接使用Future;代碼示例僅用于教育目的。你與asynccio的大部分聯(lián)系都是通過Task實(shí)例進(jìn)行的。
你可能想知道如果在Task實(shí)例上調(diào)用set_result()會(huì)發(fā)生什么。在Python 3.8之前可以這樣做,但現(xiàn)在不允許這么做了。任務(wù)實(shí)例是協(xié)程對(duì)象的包裝器,它們的結(jié)果值只能在內(nèi)部設(shè)置為底層協(xié)程函數(shù)的結(jié)果,如 示例 3-17所示那樣。
示例 3-17. 在task上調(diào)用set_result
(L13)唯一的區(qū)別是我們創(chuàng)建的是Task實(shí)例而不是Future實(shí)例。當(dāng)然,Task API要求我們提供一個(gè)協(xié)程;這里我們使用sleep()只是因?yàn)楹唵畏奖恪?/p>
(L7)正在傳入一個(gè)Task實(shí)例。它滿足函數(shù)的類型簽名(因?yàn)門ask是Future的子類),但從Python 3.8開始,我們不再允許在Task上調(diào)用set_result():嘗試這樣做將引發(fā)RuntimeError。這個(gè)想法是,一個(gè)Task代表一個(gè)正在運(yùn)行的協(xié)程,所以結(jié)果應(yīng)該總是來自于task自身。
(L10, L24)但是,我們?nèi)匀豢梢詂ancel()一個(gè)任務(wù),它將在底層協(xié)程中引發(fā)CancelledError。
Create_task? Ensure_Future? 下定決心吧!
在第22頁的“快速入門”中,我說過運(yùn)行協(xié)程的方法是使用asyncio.create_task()。在引入該函數(shù)之前,有必要獲取一個(gè)循環(huán)實(shí)例并使用loop.create_task()完成相同的任務(wù)。事實(shí)上,這也可以通過一個(gè)不同的模塊級(jí)函數(shù)來實(shí)現(xiàn):asyncio.ensure_future()。一些開發(fā)人員推薦create_task(),而其他人推薦ensure_future()。
在我為這本書做研究的過程中,我確信API方法asyncio.ensure_future()是引起對(duì)asyncio庫廣泛誤解的罪魁禍?zhǔn)住PI的大部分內(nèi)容都非常清晰,但在學(xué)習(xí)過程中還存在一些嚴(yán)重的障礙,這就是其中之一。當(dāng)你遇到ensure_future()時(shí),你的大腦會(huì)非常努力地將其集成到關(guān)于asyncio應(yīng)該如何使用的心理模型中——但很可能會(huì)失敗!
在Python 3.6 asyncio 文檔中,這個(gè)現(xiàn)在已經(jīng)臭名昭著的解釋突出了 ensure_future() 的問題:
asyncio.ensure_future(coro_or_future, *, _loop =None)
安排執(zhí)行一個(gè)協(xié)程對(duì)象:把它包裝在future中。返回一個(gè)Task對(duì)象。如果參數(shù)是Future,則直接返回。
什么!? 當(dāng)我第一次讀到這篇文章時(shí),我很困惑。下面希望是對(duì)ensure_future()的更清楚的描述:
這個(gè)函數(shù)很好地說明了針對(duì)終端用戶開發(fā)人員的asyncio API(高級(jí)API)和針對(duì)框架設(shè)計(jì)人員的asyncio API(低級(jí)API)之間的區(qū)別。讓我們?cè)谑纠?3-18中自習(xí)看看它是如何工作的。
示例 3-18. 仔細(xì)看看ensure_future()在做什么
(L3)一個(gè)簡單的什么都不做的協(xié)程函數(shù)。我們只需要一些能組成協(xié)程的東西。
(L6)我們通過直接調(diào)用該函數(shù)來創(chuàng)建協(xié)程對(duì)象。你的代碼很少會(huì)這樣做,但我想在這里明確地表示,我們正在向每個(gè)create_task()和ensure_future()傳遞一個(gè)協(xié)程對(duì)象。
(L7)獲取一個(gè)循環(huán)。
(L9)首先,我們使用loop.create_task()在循環(huán)中調(diào)度協(xié)程,并返回一個(gè)新的Task實(shí)例。
(L10)驗(yàn)證類型。到目前為止,沒有什么有趣的。
(L12)我們展示了asyncio.ensure_future()可以被用來執(zhí)行與create_task()相同的動(dòng)作:我們傳入了一個(gè)協(xié)程,并返回了一個(gè)Task實(shí)例(并且協(xié)程已經(jīng)被安排在循環(huán)中運(yùn)行)!如果傳入的是協(xié)程,那么loop.create_task()和asyncio.ensure_future()之間沒有區(qū)別。
(L15)如果我們給ensure_future()傳遞一個(gè)Task實(shí)例會(huì)發(fā)生什么呢?注意我們要傳遞的Task實(shí)例是已經(jīng)在第4步通過loop.create_task()創(chuàng)建好的。
(L16)返回的Task實(shí)例與傳入的Task實(shí)例完全相同:它在被傳遞時(shí)沒有被改變。
直接傳遞Future實(shí)例的意義何在?為什么用同一個(gè)函數(shù)做兩件不同的事情?答案是,ensure_future()的目的是讓框架作者向最終用戶開發(fā)者提供可以處理兩種參數(shù)的API。不相信我?這是ex-BDFL自己說的:
ensure_future()的要點(diǎn)是,如果你有一個(gè)可能是協(xié)程或Future(后者包括一個(gè)Task,因?yàn)樗荈uture的子類)的東西,并且你想能夠調(diào)用一個(gè)只在Future上定義的方法(可能唯一有用的例子是cancel())。當(dāng)它已經(jīng)是Future(或Task)時(shí),它什么也不做;當(dāng)它是協(xié)程時(shí),它將它包裝在Task中。
如果您知道您有一個(gè)協(xié)程,并且希望它被調(diào)度,那么正確的API是create_task()。唯一應(yīng)該調(diào)用ensure_future()的時(shí)候是當(dāng)你提供一個(gè)API(像大多數(shù)asyncio自己的API),它接受協(xié)程或Future,你需要對(duì)它做一些事情,需要你有一個(gè)Future。
—Guido van Rossum
總而言之,asyncio.sure_future()是一個(gè)為框架設(shè)計(jì)者準(zhǔn)備的輔助函數(shù)。這一點(diǎn)最容易通過與一種更常見的函數(shù)進(jìn)行類比來解釋,所以我們來做這個(gè)解釋。如果你有幾年的編程經(jīng)驗(yàn),你可能已經(jīng)見過類似于例3-19中的istify()函數(shù)的函數(shù)。示例 3-19中l(wèi)istify()的函數(shù)。
示例 3-19. 一個(gè)強(qiáng)制輸入列表的工具函數(shù)
這個(gè)函數(shù)試圖將參數(shù)轉(zhuǎn)換為一個(gè)列表,不管輸入的是什么。api和框架中經(jīng)常使用這類函數(shù)將輸入強(qiáng)制轉(zhuǎn)換為已知類型,這將簡化后續(xù)代碼——在本例中,您知道參數(shù)(來自listify()的輸出)將始終是一個(gè)列表。
如果我將listify()函數(shù)重命名為ensure_list(),那么您應(yīng)該開始看到與asyncio.ensure_future()的類似之處:它總是試圖將參數(shù)強(qiáng)制轉(zhuǎn)換為Future(或子類)類型。這是一個(gè)實(shí)用函數(shù),它使框架開發(fā)人員(而不是像你我這樣的終端用戶開發(fā)人員)的工作變得更容易。
實(shí)際上,asyncio標(biāo)準(zhǔn)庫模塊本身使用ensure_future()正是出于這個(gè)原因。當(dāng)你下次查看API時(shí),你會(huì)發(fā)現(xiàn)函數(shù)參數(shù)被描述為“可等待對(duì)象”,很可能內(nèi)部使用ensure_future()強(qiáng)制轉(zhuǎn)換參數(shù)。例如,asyncio.gather()函數(shù)就像下面的代碼一樣:
aws參數(shù)表示“可等待對(duì)象”,包括協(xié)程、task和future。在內(nèi)部,gather()使用ensure_future()進(jìn)行類型強(qiáng)制轉(zhuǎn)換:task和future保持不變,而把協(xié)程強(qiáng)制轉(zhuǎn)為task。
這里的關(guān)鍵是,作為終端用戶應(yīng)用程序開發(fā)人員,應(yīng)該永遠(yuǎn)不需要使用asyncio.ensure_future()。它更像是框架設(shè)計(jì)師的工具。如果你需要在事件循環(huán)上調(diào)度協(xié)程,只需直接使用asyncio.create_task()來完成。
在接下來的幾節(jié)中,我們將回到語言級(jí)別的特性,從異步上下文管理器開始。
setInterval(函數(shù)名,1000); t: Timer = new Timer(1000, 5); t.addEventListener(TimerEvent.TIMER,函數(shù)名); t.addEventListener(TimerEvent.TIMER_COMPLETE, 函數(shù)名); t.start();
C10k是一個(gè)在1999年被提出來的技術(shù)挑戰(zhàn),如何在一顆1GHz CPU,2G內(nèi)存,1gbps網(wǎng)絡(luò)環(huán)境下,讓單臺(tái)服務(wù)器同時(shí)為1萬個(gè)客戶端提供FTP服務(wù)
阻塞式I/O(使用最多)、非阻塞式I/O、I/O復(fù)用、信號(hào)驅(qū)動(dòng)式I/O(幾乎不使用)、異步I/O(POSIX的aio_系列函數(shù))
select、poll、epoll都是IO多路復(fù)用的機(jī)制。I/O多路復(fù)用就是通過一種機(jī)制,一個(gè)進(jìn)程可以監(jiān)聽多個(gè)描述符,一旦,某個(gè)描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫操作。但select、poll、epoll本質(zhì)上都是同步I/O,因?yàn)樗麄兌夹枰谧x寫時(shí)間就緒后負(fù)責(zé)進(jìn)行讀寫,也就是說讀寫過程是阻塞的,而異步I/O無需自己負(fù)責(zé)進(jìn)行讀寫,異步I/O的實(shí)現(xiàn)會(huì)負(fù)責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間
(1)select
select函數(shù)監(jiān)視的文件描述符分3類,分別是writefds、readfds、exceptfds。調(diào)用select函數(shù)會(huì)阻塞,直到有描述符就緒(有數(shù)據(jù)可讀、可寫或者有except),或者超時(shí)函數(shù)返回。當(dāng)select函數(shù)返回后可以通過遍歷fdset來找到就緒的描述符。
select目前幾乎在所有的平臺(tái)上支持,其良好的跨平臺(tái)支持也是它的一個(gè)優(yōu)點(diǎn)。select的一個(gè)缺點(diǎn)在于單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在Linux上一般為1024,可以通過修改宏定義甚至重新編譯內(nèi)核的方式提升這一限制,但是這樣也會(huì)降低效率。
(2)poll
不同于select使用三個(gè)位圖來表示三個(gè)fdset的方式,poll使用一個(gè)pollfd的指針實(shí)現(xiàn)。
pollfd結(jié)構(gòu)包含了要監(jiān)視的event和發(fā)生的event,不再使用select"參數(shù)-值"傳遞的方式。同時(shí)pollfd并沒有最大數(shù)量限制(但是數(shù)量過大后性能也會(huì)下降)。和select函數(shù)一樣,poll返回后,需要輪詢pollfd來獲取就緒的描述符。
從上面看,select和poll都需要在返回后通過遍歷文件描述符來獲取已經(jīng)就緒的socket。事實(shí)上同時(shí)連接的大量客戶端在同一時(shí)刻可能只有很少的處于就緒的狀態(tài),因此隨著監(jiān)視的描述符數(shù)量的增長,其效率也會(huì)線性下降
(3)epoll
epoll是在2.6內(nèi)核中提出的,是之前的select和poll的增強(qiáng)版本。相對(duì)于select和poll來說,epoll更加領(lǐng)靈活,沒有描述符限制。epoll使用一個(gè)文件描述符管理多個(gè)描述符,將用戶關(guān)系的文件描述符的事件存放到內(nèi)核的一個(gè)事件表中,這樣在用戶空間和內(nèi)核空間的copy只需一次。
名稱欄目:python異步函數(shù)中,python 異步線程
新聞來源:http://chinadenli.net/article3/dsggjis.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、網(wǎng)站設(shè)計(jì)公司、微信小程序、手機(jī)網(wǎng)站建設(shè)、定制開發(fā)、網(wǎng)頁設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)