乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Python異步IO系列:協(xié)程走起來(lái)!

       鷹兔牛熊眼 2019-01-30

      前面,我們從大的結(jié)構(gòu)上認(rèn)識(shí)了最新的Python 3.7里面的asyncio標(biāo)準(zhǔn)庫(kù)。接下來(lái),我們就開(kāi)始一點(diǎn)一點(diǎn)的來(lái)學(xué)習(xí)asyncio的使用。


      一、安裝 Python 3.7


      我的系統(tǒng)是 Ubuntu 16.04,里面有 Python 2.7 和 Python 3.6。2.7是系統(tǒng)自帶的,其它系統(tǒng)軟件對(duì)此有依賴,不能破壞。3.6 是通過(guò)ppa源apt install的,然后通過(guò) virtualenvwrapper 管理虛擬環(huán)境。同樣的,安裝3.7也是用ppa源。


      sudo add-apt-repository ppa:deadsnakes/ppasudo apt update
      sudo apt install python3.7 python3.7-dev

      安裝python3.7-dev包的目的是編譯C/C++模塊時(shí)需要Python.h等頭文件。安裝好后,建立一個(gè)Python 3.7 的虛擬環(huán)境,就叫 py3.7 吧:


      mkvirtualenv -p python3.7 py3.7


      建立好虛擬環(huán)境,通過(guò)命令:workon py3.7 就可以使用 3.7 了。


      Windows 的安裝可以移步猿人學(xué)網(wǎng)站,閱讀《Python開(kāi)發(fā)環(huán)境的安裝配置》這篇文章,里面有詳細(xì)說(shuō)明。


      二、創(chuàng)建第一個(gè)協(xié)程


      Python 3.7 推薦使用 async/await 語(yǔ)法來(lái)聲明協(xié)程,來(lái)編寫(xiě)異步應(yīng)用程序。我們來(lái)創(chuàng)建第一個(gè)協(xié)程函數(shù):首先打印一行“你好”,等待1秒鐘后再打印“猿人學(xué)”。



      sayhi()函數(shù)通過(guò) async 聲明為協(xié)程函數(shù),較之前的修飾器聲明更簡(jiǎn)潔明了。


      在實(shí)踐過(guò)程中,什么功能的函數(shù)要用async聲明為協(xié)程函數(shù)呢?就是那些能發(fā)揮異步IO性能的函數(shù),比如讀寫(xiě)文件、讀寫(xiě)網(wǎng)絡(luò)、讀寫(xiě)數(shù)據(jù)庫(kù),這些都是浪費(fèi)時(shí)間的IO操作,把它們協(xié)程化、異步化從而提高程序的整體效率(速度)。


      sayhi()函數(shù)是通過(guò) asyncio.run()來(lái)運(yùn)行的,而不是直接調(diào)用這個(gè)函數(shù)(協(xié)程)。因?yàn)?,直接調(diào)用并不會(huì)把它加入調(diào)度日程,而只是簡(jiǎn)單的返回一個(gè)協(xié)程對(duì)象:



      那么,如何真正運(yùn)行一個(gè)協(xié)程呢?asyncio 提供了三種機(jī)制:


      (1)asyncio.run() 函數(shù),這是異步程序的主入口,相當(dāng)于C語(yǔ)言中的main函數(shù)。


      (2)用await等待協(xié)程,比如上例中的 await asyncio.sleep(1) 。再看下面的例子,我們定義了協(xié)程 say_delay() ,在main()協(xié)程中調(diào)用兩次,第一次延遲1秒后打印“你好”,第二次延遲2秒后打印“猿人學(xué)”。這樣我們通過(guò) await 運(yùn)行了兩個(gè)協(xié)程。

      從起止時(shí)間可以看出,兩個(gè)協(xié)程是順序執(zhí)行的,總共耗時(shí)1+2=3秒。


      (3)通過(guò) asyncio.create_task() 函數(shù)并發(fā)運(yùn)行作為 asyncio 任務(wù)(Task) 的多個(gè)協(xié)程。下面,我們用create_task()來(lái)修改上面的main()協(xié)程,從而讓兩個(gè)say_delay()協(xié)程并發(fā)運(yùn)行:

      從運(yùn)行結(jié)果的起止時(shí)間可以看出,兩個(gè)協(xié)程是并發(fā)執(zhí)行的了,總耗時(shí)等于最大耗時(shí)2秒。


      asyncio.create_task() 是一個(gè)很有用的函數(shù),在爬蟲(chóng)中它可以幫助我們實(shí)現(xiàn)大量并發(fā)去下載網(wǎng)頁(yè)。在Python 3.6中與它對(duì)應(yīng)的是 ensure_future()。


      三、可等待對(duì)象(awaitables)


      可等待對(duì)象,就是可以在 await 表達(dá)式中使用的對(duì)象,前面我們已經(jīng)接觸了兩種可等待對(duì)象的類型:協(xié)程和任務(wù),還有一個(gè)是低層級(jí)的Future。


      asyncio模塊的許多API都需要傳入可等待對(duì)象,比如 run(), create_task() 等等。


      (1)協(xié)程

      協(xié)程是可等待對(duì)象,可以在其它協(xié)程中被等待。協(xié)程兩個(gè)緊密相關(guān)的概念是:


      • 協(xié)程函數(shù):通過(guò) async def 定義的函數(shù);

      • 協(xié)程對(duì)象:調(diào)用協(xié)程函數(shù)返回的對(duì)象。


      運(yùn)行上面這段程序,結(jié)果為:

      co is  <class 'coroutine'>

      now is 1548512708.2026224

      now is 1548512708.202648

      可以看到,直接運(yùn)行協(xié)程函數(shù) whattime()得到的co是一個(gè)協(xié)程對(duì)象,因?yàn)閰f(xié)程對(duì)象是可等待的,所以通過(guò) await 得到真正的當(dāng)前時(shí)間。now2是直接await 協(xié)程函數(shù),也得到了當(dāng)前時(shí)間的返回值。


      (2)任務(wù)


      前面我們講到,任務(wù)是用來(lái)調(diào)度協(xié)程的,以便并發(fā)執(zhí)行協(xié)程。當(dāng)一個(gè)協(xié)程通過(guò) asyncio.create_task() 被打包為一個(gè) 任務(wù),該協(xié)程將自動(dòng)加入程序調(diào)度日程準(zhǔn)備立即運(yùn)行。


      create_task()的基本使用前面例子已經(jīng)講過(guò)。它返回的task通過(guò)await來(lái)等待其運(yùn)行完。如果,我們不等待,會(huì)發(fā)生什么?“準(zhǔn)備立即運(yùn)行”又該如何理解呢?先看看下面這個(gè)例子:

      運(yùn)行這段代碼的情況是這樣的:

      首先,1秒鐘后打印一行,這是第13,14行代碼運(yùn)行的結(jié)果:

      calling:0, now is 09:15:15 

      接著,停頓1秒后,連續(xù)打印4行:

      calling:1, now is 09:15:16

      calling:2, now is 09:15:16

      calling:3, now is 09:15:16

      calling:4, now is 09:15:16

      從這個(gè)結(jié)果看,asyncio.create_task()產(chǎn)生的4個(gè)任務(wù),我們并沒(méi)有await,它們也執(zhí)行了。關(guān)鍵在于第18行的 await,如果把這一行去掉或是sleep的時(shí)間小于1秒(比whattime()里面的sleep時(shí)間少即可),就會(huì)只看到第一行的輸出結(jié)果而看不到后面四行的輸出。這是因?yàn)?,main()不sleep或sleep少于1秒鐘,main()就在whattime()還未來(lái)得及打印結(jié)果(因?yàn)?,它要sleep1秒)就退出了,從而整個(gè)程序也退出了,就沒(méi)有whattime()的輸出結(jié)果。


      再來(lái)理解一下“準(zhǔn)備立即執(zhí)行”這個(gè)說(shuō)法。它的意思就是,create_task()只是打包了協(xié)程并加入調(diào)度隊(duì)列還未執(zhí)行,并準(zhǔn)備立即執(zhí)行,什么時(shí)候執(zhí)行呢?在“主協(xié)程”(調(diào)用create_task()的協(xié)程)掛起的時(shí)候,這里的“掛起”有兩個(gè)方式:


      一是,通過(guò) await task 來(lái)執(zhí)行這個(gè)任務(wù);

      另一個(gè)是,主協(xié)程通過(guò) await sleep 掛起,事件循環(huán)就去執(zhí)行task了。


      我們知道,asyncio是通過(guò)事件循環(huán)實(shí)現(xiàn)異步的。在主協(xié)程 main()里面,沒(méi)有遇到 await 時(shí),事件就是執(zhí)行main()函數(shù),遇到 await 時(shí),事件循環(huán)就去執(zhí)行別的協(xié)程,即create_task()生成的whattime()的4個(gè)任務(wù),這些任務(wù)一開(kāi)始就是 await sleep 1秒。這時(shí)候,主協(xié)程和4個(gè)任務(wù)協(xié)程都掛起了,CPU空閑,事件循環(huán)等待協(xié)程的消息。


      如果main()協(xié)程只sleep了0.1秒,它就先醒了,給事件循環(huán)發(fā)消息,事件循環(huán)就來(lái)繼續(xù)執(zhí)行main()協(xié)程,而main()后面已經(jīng)沒(méi)有代碼,就退出該協(xié)程,退出它也就意味著整個(gè)程序退出,4個(gè)任務(wù)就沒(méi)機(jī)會(huì)打印結(jié)果;


      如果main()協(xié)程sleep時(shí)間多余1秒,那么4個(gè)任務(wù)先喚醒,就會(huì)得到全部的打印結(jié)果;


      如果main()的18行sleep等于1秒時(shí),和4個(gè)任務(wù)的sleep時(shí)間相同,也會(huì)得到全部打印結(jié)果。這是為什么呢?


      我猜想是這樣的:4個(gè)任務(wù)生成在前,第18行的sleep在后,事件循環(huán)的消息響應(yīng)可能有個(gè)先進(jìn)先出的順序。后面深入asyncio的代碼專門(mén)研究一下這個(gè)猜想正確與否。


      (3)Future

      它是一個(gè)低層級(jí)的可等待對(duì)象,表示一個(gè)異步操作的最終結(jié)果。目前,我們寫(xiě)應(yīng)用程序還用不到它,暫不學(xué)習(xí)。


      總結(jié)


      協(xié)程就是我們異步操作的片段。通常,寫(xiě)程序都會(huì)把全部功能分成很多不同功能的函數(shù),目的是為了結(jié)構(gòu)清晰;進(jìn)一步,把那些涉及耗費(fèi)時(shí)間的IO操作(讀寫(xiě)文件、數(shù)據(jù)庫(kù)、網(wǎng)絡(luò))的函數(shù)通過(guò) async def 異步化,就是異步編程。


      那些異步函數(shù)(協(xié)程函數(shù))都是通過(guò)消息機(jī)制被事件循環(huán)管理調(diào)度著,整個(gè)程序的執(zhí)行是單線程的,但是某個(gè)協(xié)程A進(jìn)行IO時(shí),事件循環(huán)就去執(zhí)行其它協(xié)程非IO的代碼。當(dāng)事件循環(huán)收到協(xié)程A結(jié)束IO的消息時(shí),就又回來(lái)執(zhí)行協(xié)程A,這樣事件循環(huán)不斷在協(xié)程之間轉(zhuǎn)換,充分利用了IO的閑置時(shí)間,從而并發(fā)的進(jìn)行多個(gè)IO操作,這就是異步IO。


      寫(xiě)異步IO程序時(shí)記住一個(gè)準(zhǔn)則:需要IO的地方異步。其它地方即使用了協(xié)程函數(shù)也是沒(méi)用的。


      網(wǎng)絡(luò)爬蟲(chóng)就是異步IO的用武之地,接下來(lái)的文章,我們就來(lái)實(shí)現(xiàn)一個(gè)異步IO爬蟲(chóng),敬請(qǐng)期待。

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多