日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Python協(xié)程:概念及其用法

真正有知識的人的成長過程,就像麥穗的成長過程:麥穗空的時候,麥子長得很快,麥穗驕傲地高高昂起,但是,麥穗成熟飽滿時,它們開始謙虛,垂下麥芒。

弋江ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!

——蒙田《蒙田隨筆全集》

上篇《Python 多線程雞年不雞肋》論述了關于python多線程是否是雞肋的問題,得到了一些網(wǎng)友的認可,當然也有一些不同意見,表示協(xié)程比多線程不知強多少,在協(xié)程面前多線程算是雞肋。好吧,對此我也表示贊同,然而上篇我論述的觀點不在于多線程與協(xié)程的比較,而是在于IO密集型程序中,多線程尚有用武之地。

對于協(xié)程,我表示其效率確非多線程能比,但本人對此了解并不深入,因此最近幾日參考了一些資料,學習整理了一番,在此分享出來僅供大家參考,如有謬誤請指正,多謝。申明:本文介紹的協(xié)程是入門級別,大神請繞道而行,謹防入坑。

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

協(xié)程

概念

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

優(yōu)勢

  • 執(zhí)行效率極高,因為子程序切換(函數(shù))不是線程切換,由程序自身控制,沒有切換線程的開銷。所以與多線程相比,線程的數(shù)量越多,協(xié)程性能的優(yōu)勢越明顯。
  • 不需要多線程的鎖機制,因為只有一個線程,也不存在同時寫變量沖突,在控制共享資源時也不需要加鎖,因此執(zhí)行效率高很多。

說明:協(xié)程可以處理IO密集型程序的效率問題,但是處理CPU密集型不是它的長處,如要充分發(fā)揮CPU利用率可以結(jié)合多進程+協(xié)程。

以上只是協(xié)程的一些概念,可能聽起來比較抽象,那么我結(jié)合代碼講一講吧。這里主要介紹協(xié)程在Python的應用,Python2對協(xié)程的支持比較有限,生成器的yield實現(xiàn)了一部分但不完全,gevent模塊倒是有比較好的實現(xiàn);Python3.4以后引入了asyncio模塊,可以很好的使用協(xié)程。

Python2.x協(xié)程

python2.x協(xié)程應用:

  • yield
  • gevent

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

Gevent

gevent是第三方庫,通過greenlet實現(xiàn)協(xié)程,其基本思想:

當一個greenlet遇到IO操作時,比如訪問網(wǎng)絡,就自動切換到其他的greenlet,等到IO操作完成,再在適當?shù)臅r候切換回來繼續(xù)執(zhí)行。由于IO操作非常耗時,經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動切換協(xié)程,就保證總有greenlet在運行,而不是等待IO。

Install

 
 
 
 
  1. pip install gevent 

***版貌似支持windows了,之前測試好像windows上運行不了……

Usage

首先來看一個簡單的爬蟲例子:

 
 
 
 
  1. #! -*- coding:utf-8 -*- 
  2.  
  3. import gevent 
  4.  
  5. from gevent import monkey;monkey.patch_all() 
  6.  
  7. import urllib2 
  8.  
  9. def get_body(i): 
  10.  
  11. print "start",i 
  12.  
  13. urllib2.urlopen("http://cn.bing.com") 
  14.  
  15. print "end",i 
  16.  
  17. tasks=[gevent.spawn(get_body,i) for i in range(3)] 
  18.  
  19. gevent.joinall(tasks)  

運行結(jié)果:

 
 
 
 
  1. start 0 
  2.  
  3. start 1 
  4.  
  5. start 2 
  6.  
  7. end 2 
  8.  
  9. end 0 
  10.  
  11. end 1  

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

換成多線程的代碼看看:

 
 
 
 
  1. import threading 
  2.  
  3. import urllib2 
  4.  
  5. def get_body(i): 
  6.  
  7. print "start",i 
  8.  
  9. urllib2.urlopen("http://cn.bing.com") 
  10.  
  11. print "end",i 
  12.  
  13. for i in range(3): 
  14.  
  15. t=threading.Thread(target=get_body,args=(i,)) 
  16.  
  17. t.start()  

運行結(jié)果:

 
 
 
 
  1. start 0 
  2.  
  3. start 1 
  4.  
  5. start 2 
  6.  
  7. end 1 
  8.  
  9. end 2 
  10.  
  11. end 0  

說明:從結(jié)果來看,多線程與協(xié)程的效果一樣,都是達到了IO阻塞時切換的功能。不同的是,多線程切換的是線程(線程間切換),協(xié)程切換的是上下文(可以理解為執(zhí)行的函數(shù))。而切換線程的開銷明顯是要大于切換上下文的開銷,因此當線程越多,協(xié)程的效率就越比多線程的高。(猜想多進程的切換開銷應該是***的)

Gevent使用說明

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

Python3.x協(xié)程

為了測試Python3.x下的協(xié)程應用,我在virtualenv下安裝了python3.6的環(huán)境。

python3.x協(xié)程應用:

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

Python3.4以后引入了asyncio模塊,可以很好的支持協(xié)程。

asynico

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

Usage

例子:(需在python3.4以后版本使用)

 
 
 
 
  1. import asyncio 
  2.  
  3. @asyncio.coroutine 
  4.  
  5. def test(i): 
  6.  
  7. print("test_1",i) 
  8.  
  9. r=yield from asyncio.sleep(1) 
  10.  
  11. print("test_2",i) 
  12.  
  13. loop=asyncio.get_event_loop() 
  14.  
  15. tasks=[test(i) for i in range(5)] 
  16.  
  17. loop.run_until_complete(asyncio.wait(tasks)) 
  18.  
  19. loop.close()  

運行結(jié)果:

 
 
 
 
  1. test_1 3 
  2.  
  3. test_1 4 
  4.  
  5. test_1 0 
  6.  
  7. test_1 1 
  8.  
  9. test_1 2 
  10.  
  11. test_2 3 
  12.  
  13. test_2 0 
  14.  
  15. test_2 2 
  16.  
  17. test_2 4 
  18.  
  19. test_2 1  

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

asyncio說明

@asyncio.coroutine把一個generator標記為coroutine類型,然后,我們就把這個coroutine扔到EventLoop中執(zhí)行。

test()會首先打印出test_1,然后,yield from語法可以讓我們方便地調(diào)用另一個generator。由于asyncio.sleep()也是一個coroutine,所以線程不會等待asyncio.sleep(),而是直接中斷并執(zhí)行下一個消息循環(huán)。當asyncio.sleep()返回時,線程就可以從yield from拿到返回值(此處是None),然后接著執(zhí)行下一行語句。

把asyncio.sleep(1)看成是一個耗時1秒的IO操作,在此期間,主線程并未等待,而是去執(zhí)行EventLoop中其他可以執(zhí)行的coroutine了,因此可以實現(xiàn)并發(fā)執(zhí)行。

asynico/await

為了簡化并更好地標識異步IO,從Python 3.5開始引入了新的語法async和await,可以讓coroutine的代碼更簡潔易讀。

請注意,async和await是針對coroutine的新語法,要使用新的語法,只需要做兩步簡單的替換:

把@asyncio.coroutine替換為async;

把yield from替換為await。

Usage

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

 
 
 
 
  1. import asyncio 
  2.  
  3. async def test(i): 
  4.  
  5. print("test_1",i) 
  6.  
  7. await asyncio.sleep(1) 
  8.  
  9. print("test_2",i) 
  10.  
  11. loop=asyncio.get_event_loop() 
  12.  
  13. tasks=[test(i) for i in range(5)] 
  14.  
  15. loop.run_until_complete(asyncio.wait(tasks)) 
  16.  
  17. loop.close()  

運行結(jié)果與之前一致。

說明:與前一節(jié)相比,這里只是把yield from換成了await,@asyncio.coroutine換成了async,其余不變。

gevent

同python2.x用法一樣。

協(xié)程VS多線程

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

異步爬蟲

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

給出幾個我所了解的方案:

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

協(xié)程池

作用:控制協(xié)程數(shù)量

 
 
 
 
  1. from bs4 import BeautifulSoup 
  2.  
  3. import requests 
  4.  
  5. import gevent 
  6.  
  7. from gevent import monkey, pool 
  8.  
  9. monkey.patch_all() 
  10.  
  11. jobs = [] 
  12.  
  13. links = [] 
  14.  
  15. p = pool.Pool(10) 
  16.  
  17. urls = [ 
  18.  
  19.     'http://www.google.com', 
  20.  
  21.     # ... another 100 urls 
  22.  
  23.  
  24. def get_links(url): 
  25.  
  26.     r = requests.get(url) 
  27.  
  28.     if r.status_code == 200: 
  29.  
  30.         soup = BeautifulSoup(r.text) 
  31.  
  32.         links + soup.find_all('a') 
  33.  
  34. for url in urls: 
  35.  
  36.     jobs.append(p.spawn(get_links, url)) 
  37.  
  38. gevent.joinall(jobs)  

本文都是一些自學時的筆記,分享給新手朋友,僅供參考

文章學習通道:

  • Python多進程 (http://python.jobbole.com/87760/)
  • Python多線程 (http://python.jobbole.com/87772/) 

本文題目:Python協(xié)程:概念及其用法
轉(zhuǎn)載來源:http://www.5511xx.com/article/dhjhjsc.html