這篇文章將為大家詳細(xì)講解有關(guān)python中協(xié)程的示例分析,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
站在用戶(hù)的角度思考問(wèn)題,與客戶(hù)深入溝通,找到橫縣網(wǎng)站設(shè)計(jì)與橫縣網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶(hù)體驗(yàn)好的作品,建站類(lèi)型包括:成都做網(wǎng)站、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、國(guó)際域名空間、網(wǎng)頁(yè)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋橫縣地區(qū)。先介紹下什么是協(xié)程:
協(xié)程,又稱(chēng)微線(xiàn)程,纖程,英文名Coroutine。協(xié)程的作用,是在執(zhí)行函數(shù)A時(shí),可以隨時(shí)中斷,去執(zhí)行函數(shù)B,然后中斷繼續(xù)執(zhí)行函數(shù)A(可以自由切換)。但這一過(guò)程并不是函數(shù)調(diào)用(沒(méi)有調(diào)用語(yǔ)句),這一整個(gè)過(guò)程看似像多線(xiàn)程,然而協(xié)程只有一個(gè)線(xiàn)程執(zhí)行。
是不是有點(diǎn)沒(méi)看懂,沒(méi)事,我們下面會(huì)解釋。要理解協(xié)程是什么,首先需要理解yield,這里簡(jiǎn)單介紹下,yield可以理解為生成器,yield item這行代碼會(huì)產(chǎn)出一個(gè)值,提供給next(...)的調(diào)用方; 此外,還會(huì)作出讓步,暫停執(zhí)行生成器,讓調(diào)用方繼續(xù)工作,直到需要使用另一個(gè)值時(shí)再調(diào)用next()。調(diào)用方會(huì)從生成器中拉取值,但是在協(xié)程中,yield關(guān)鍵字一般是在表達(dá)式右邊(如,data=yield),協(xié)程可以從調(diào)用方接收數(shù)據(jù),也可以產(chǎn)出數(shù)據(jù),下面看一個(gè)簡(jiǎn)單的例子:
>>> def simple_coroutine(): ... print('coroutine start') ... x = yield ... print('coroutine recive:',x) ... >>> my_co=simple_coroutine() >>> my_co>>> next(my_co) coroutine start >>> my_co.send(42) coroutine recive: 42 Traceback (most recent call last): File "", line 1, in StopIteration
其中x = yield就是精髓部分,意思是從客戶(hù)端獲取數(shù)據(jù),產(chǎn)出None,因?yàn)閥ield關(guān)鍵字右邊沒(méi)有表達(dá)式, 而協(xié)程在創(chuàng)建完成之后,是沒(méi)有啟動(dòng)的,沒(méi)有在yield處暫停,所以需要調(diào)用next()函數(shù),啟動(dòng)協(xié)程,在調(diào)用my_co.send(42)之后,協(xié)程定義體中的yield表達(dá)式會(huì)計(jì)算出42,現(xiàn)在協(xié)程恢復(fù),一直運(yùn)行到下一個(gè)yield表達(dá)式,或者終止,在最后,控制權(quán)流動(dòng)到協(xié)程定義體的末尾,生成器拋出StopIteration異常。
協(xié)程有四個(gè)狀態(tài),如下:
'GEN_CREATED' 等待開(kāi)始執(zhí)行。
'GEN_RUNNING' 解釋器正在執(zhí)行。
'GEN_SUSPENDED' 在 yield 表達(dá)式處暫停。
'GEN_CLOSED' 執(zhí)行結(jié)束。
當(dāng)前狀態(tài)可以使用inspect.getgeneratorstate來(lái)確定,如下:
>>> import inspect >>> inspect.getgeneratorstate(my_co) 'GEN_CLOSED'
這里再解釋下next(my_co),如果在創(chuàng)建好協(xié)程對(duì)象之后,立即把None之外的值發(fā)送給它,會(huì)出現(xiàn)如下錯(cuò)誤:
>>> my_co=simple_coroutine() >>> my_co.send(42) Traceback (most recent call last): File "", line 1, inTypeError: can't send non-None value to a just-started generator >>> my_co=simple_coroutine() >>> my_co.send(None) coroutine start
最先調(diào)用 next(my_co) 函數(shù)這一步通常稱(chēng)為“預(yù)激”(prime)協(xié)程(即,讓協(xié)程向前執(zhí)行到第一個(gè) yield 表達(dá)式,準(zhǔn)備好作為活躍的協(xié)程使用)。
再參考下面這個(gè)例子:
>>> def simple_coro2(a): ... print('-> Started: a =', a) ... b = yield a ... print('-> Received: b =', b) ... c = yield a + b ... print('-> Received: c =', c) ... >>> my_coro2 = simple_coro2(14) >>> from inspect import getgeneratorstate >>> getgeneratorstate(my_coro2) 'GEN_CREATED' >>> next(my_coro2) # 協(xié)程執(zhí)行到`b = yield a`處暫停,等待為b賦值, -> Started: a = 14 14 >>> getgeneratorstate(my_coro2) 'GEN_SUSPENDED' #從狀態(tài)也可以看到,當(dāng)前是暫停狀態(tài)。 >>> my_coro2.send(28) #將28發(fā)送到協(xié)程,計(jì)算yield表達(dá)式,并把結(jié)果綁定到b,產(chǎn)出a+b的值,然后暫停。 -> Received: b = 28 42 >>> my_coro2.send(99) -> Received: c = 99 Traceback (most recent call last): File "", line 1, inStopIteration >>> getgeneratorstate(my_coro2) 'GEN_CLOSED'
simple_coro2的執(zhí)行過(guò)程如下圖所示:
調(diào)用next(my_coro2),打印第一個(gè)消息,然后執(zhí)行yield a,產(chǎn)出數(shù)字 14。
調(diào)用my_coro2.send(28),把28賦值給b,打印第二個(gè)消息,然后執(zhí)行yield a + b,產(chǎn) 出數(shù)字 42。
調(diào)用my_coro2.send(99),把 99 賦值給 c,打印第三個(gè)消息,協(xié)程終止。
說(shuō)了這么多,我們?yōu)槭裁匆脜f(xié)程呢,下面我們?cè)倏纯此膬?yōu)勢(shì)是什么:
執(zhí)行效率極高,因?yàn)樽映绦蚯袚Q(函數(shù))不是線(xiàn)程切換,由程序自身控制,沒(méi)有切換線(xiàn)程的開(kāi)銷(xiāo)。所以與多線(xiàn)程相比,線(xiàn)程的數(shù)量越多,協(xié)程性能的優(yōu)勢(shì)越明顯。
不需要多線(xiàn)程的鎖機(jī)制,因?yàn)橹挥幸粋€(gè)線(xiàn)程,也不存在同時(shí)寫(xiě)變量沖突,在控制共享資源時(shí)也不需要加鎖,因此執(zhí)行效率高很多。
說(shuō)明:協(xié)程可以處理IO密集型程序的效率問(wèn)題,但是處理CPU密集型不是它的長(zhǎng)處,如要充分發(fā)揮CPU利用率可以結(jié)合多進(jìn)程+協(xié)程。
下面看最后一個(gè)例子,傳統(tǒng)的生產(chǎn)者-消費(fèi)者模型是一個(gè)線(xiàn)程寫(xiě)消息,一個(gè)線(xiàn)程取消息,通過(guò)鎖機(jī)制控制隊(duì)列和等待,但一不小心就可能死鎖。
如果改用協(xié)程,生產(chǎn)者生產(chǎn)消息后,直接通過(guò)yield跳轉(zhuǎn)到消費(fèi)者開(kāi)始執(zhí)行,待消費(fèi)者執(zhí)行完畢后,切換回生產(chǎn)者繼續(xù)生產(chǎn),效率極高:
from bs4 import BeautifulSoup import requests from urllib.parse import urlparse start_url = 'https://www.cnblogs.com' trust_host = 'www.cnblogs.com' ignore_path = [] history_urls = [] def parse_html(html): soup = BeautifulSoup(html, "lxml") print(soup.title) links = soup.find_all('a', href=True) return (a['href'] for a in links if a['href']) def parse_url(url): url = url.strip() if url.find('#') >= 0: url = url.split('#')[0] if not url: return None if url.find('javascript:') >= 0: return None for f in ignore_path: if f in url: return None if url.find('http') < 0: url = start_url + url return url parse = urlparse(url) if parse.hostname == trust_host: return url def consumer(): html = '' while True: url = yield html if url: print('[CONSUMER] Consuming %s...' % url) rsp = requests.get(url) html = rsp.content def produce(c): next(c) def do_work(urls): for u in urls: if u not in history_urls: history_urls.append(u) print('[PRODUCER] Producing %s...' % u) html = c.send(u) results = parse_html(html) work_urls = (x for x in map(parse_url, results) if x) do_work(work_urls) do_work([start_url]) c.close() if __name__ == '__main__': c = consumer() produce(c) print(len(history_urls))
首先consumer函數(shù)是一個(gè)generator,在開(kāi)始執(zhí)行之后:
調(diào)用next(c)啟動(dòng)生成器;
進(jìn)入do_work,這是一個(gè)遞歸調(diào)用,其內(nèi)部將url傳遞給consumer,由consumer來(lái)發(fā)出請(qǐng)求,獲取到html信息,返回給produce,
produce解析html,獲取url數(shù)據(jù),繼續(xù)生產(chǎn)url,
當(dāng)所有的url都在history_urls中,也就是說(shuō)我們已經(jīng)爬取了所有的url地址,結(jié)束遞歸調(diào)用
調(diào)用c.close(),關(guān)閉consumer,整個(gè)過(guò)程結(jié)束。
可以看到,我們的整個(gè)流程無(wú)鎖,由一個(gè)線(xiàn)程執(zhí)行,produce和consumer協(xié)作完成任務(wù),所以稱(chēng)為“協(xié)程”,而非線(xiàn)程的搶占式多任務(wù)。
關(guān)于“python中協(xié)程的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。