協程的概念很早就提出來了,但直到最近幾年纔在某些語言(如Lua)中獲得普遍應用。bash
子程序,或者稱爲函數,在全部語言中都是層級調用,好比A調用B,B在執行過程當中又調用了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。多線程
因此子程序調用是經過棧實現的,一個線程就是執行一個子程序。併發
子程序調用老是一個入口,一次返回,調用順序是明確的。而協程的調用和子程序不一樣。app
協程看上去也是子程序,但執行過程當中,在子程序內部可中斷,而後轉而執行別的子程序,在適當的時候再返回來接着執行。異步
注意,在一個子程序中中斷,去執行其餘子程序,不是函數調用,有點相似CPU的中斷。函數
摘自網上測試
協程與多線程的比較,能夠參考下圖url
通常在Python討論協程時,都會與生成器聯繫在一塊兒spa
生成器是一個函數,主要特色是生成器在返回值是,不是使用return,而是使用yield關鍵字,在定義函數時,若是函數體中包含yield關鍵字,則該函數就被認爲是一個生成器,對於這些基本概念,咱們不作過多討論
接下來咱們首先要引入一個模塊gevent,使用gevent異步庫能夠更加方便地實現基於協程的併發設計,在gevent中使用greenlet對象實現併發,greenlet就是協程,能夠將其認爲是一種輕量線程
首先安裝這個模塊,很是簡單
pip3 install gevent
接下來,咱們仍是以以前噹噹圖書的那個例子,進行測試
咱們在這定義協程的主要函數
# -*- coding: utf-8 -*- """ Created on 2018/5/5 @author: susmote """ import gevent from gevent import monkey monkey.patch_all() import time import mining_func def gevent_test(): start_time = time.time() page_range_list = [ (1, 10), (11, 20), (21, 32), ] jobs = [] for page_range in page_range_list: jobs.append(gevent.spawn(mining_func.get_urls_in_pages, page_range[0], page_range[1])) gevent.joinall(jobs) end_time = time.time() print("抓取時間:", end_time - start_time) return end_time - start_time
關於這段代碼,基本和以前定義多線程,多進程的過程類似,我不作過多無用的解釋,只是提示一下,monkey.patch_all()這個是必須不能忘記加,若是沒有這一句,程序將會變爲依次順序抓取,這樣就會失去併發的能力
gevent.spawn這段語句能夠生成greenlet,gevent.joinall(jobs),也就是說他會阻塞程序的執行,直至全部的協程執行完畢
運行主函數以下
# -*- coding: utf-8 -*- """ Created on 2018/5/5 @author: susmote """ from main_func import gevent_test if __name__ == "__main__": gevent_test()
下面運行這段代碼
最後運行時間
3.439 秒
關於協程我講的就是這些