1. 程式人生 > >誰說Python協程是雞肋的!站出來我不打死他!這麽牛逼的協程!

誰說Python協程是雞肋的!站出來我不打死他!這麽牛逼的協程!

版本 完成 由於 操作 sage cio 文章 換上 訪問

技術分享圖片

文章思路:本文將先介紹協程的概念,然後分別介紹Python2.x與3.x下協程的用法,最終將協程與多線程做比較並介紹異步爬蟲模塊。

協程

概念

協程,又稱微線程,纖程,英文名Coroutine。協程的作用,是在執行函數A時,可以隨時中斷,去執行函數B,然後中斷繼續執行函數A(可以自由切換)。但這一過程並不是函數調用(沒有調用語句),這一整個過程看似像多線程,然而協程只有一個線程執行。

進群:548377875 即可獲取數十套PDF哦!

技術分享圖片

Python2.x協程

技術分享圖片

python2.x協程應用:

  • yield
  • gevent

python2.x中支持協程的模塊不多,gevent算是比較常用的,這裏就簡單介紹一下gevent的用法。

Gevent

gevent是第三方庫,通過greenlet實現協程,其基本思想:

當一個greenlet遇到IO操作時,比如訪問網絡,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。由於IO操作非常耗時,經常使程序處於等待狀態,有了gevent為我們自動切換協程,就保證總有greenlet在運行,而不是等待IO。

技術分享圖片

運行結果:

start 0

start 1

start 2

end 2

end 0

end 1

說明:從結果上來看,執行get_body的順序應該先是輸出”start”,然後執行到urllib2時碰到IO堵塞,則會自動切換運行下一個程序(繼續執行get_body輸出start),直到urllib2返回結果,再執行end。也就是說,程序沒有等待urllib2請求網站返回結果,而是直接先跳過了,等待執行完畢再回來獲取返回值。值得一提的是,在此過程中,只有一個線程在執行,因此這與多線程的概念是不一樣的。

技術分享圖片

Gevent使用說明

  • monkey可以使一些阻塞的模塊變得不阻塞,機制:遇到IO操作則自動切換,手動切換可以用gevent.sleep(0)(將爬蟲代碼換成這個,效果一樣可以達到切換上下文)
  • gevent.spawn 啟動協程,參數為函數名稱,參數名稱
  • gevent.joinall 停止協程

Python3.x協程

為了測試Python3.x下的協程應用,我在virtualenv下安裝了python3.6的環境。

python3.x協程應用:

  • asynico + yield from(python3.4)
  • asynico + await(python3.5)
  • gevent

Python3.4以後引入了asyncio模塊,可以很好的支持協程。

asynico

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

技術分享圖片

說明:從運行結果可以看到,跟gevent達到的效果一樣,也是在遇到IO操作時進行切換(所以先輸出test_1,等test_1輸出完再輸出test_2)。但此處我有一點不明,test_1的輸出為什麽不是按照順序執行的呢?可以對比gevent的輸出結果(希望大神能解答一下)。

技術分享圖片

Usage

例子(python3.5以後版本使用):

技術分享圖片

gevent

同python2.x用法一樣。

協程VS多線程

如果通過以上介紹,你已經明白多線程與協程的不同之處,那麽我想測試也就沒有必要了。因為當線程越來越多時,多線程主要的開銷花費在線程切換上,而協程是在一個線程內切換的,因此開銷小很多,這也許就是兩者性能的根本差異之處吧。(個人觀點)

異步爬蟲

也許關心協程的朋友,大部分是用其寫爬蟲(因為協程能很好的解決IO阻塞問題),然而我發現常用的urllib、requests無法與asyncio結合使用,可能是因為爬蟲模塊本身是同步的(也可能是我沒找到用法)。那麽對於異步爬蟲的需求,又該怎麽使用協程呢?或者說怎麽編寫異步爬蟲?

  • grequests (requests模塊的異步化)
  • 爬蟲模塊+gevent(比較推薦這個)
  • aiohttp (這個貌似資料不多,目前我也不太會用)
  • asyncio內置爬蟲功能 (這個也比較難用)

協程池

作用:控制協程數量

技術分享圖片

get去吧!

誰說Python協程是雞肋的!站出來我不打死他!這麽牛逼的協程!