欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python中的協(xié)程深入理解

 更新時(shí)間:2019年06月10日 09:10:51   作者:且聽(tīng)風(fēng)吟  
這篇文章主要給大家介紹了關(guān)于python中協(xié)程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用python具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

先介紹下什么是協(xié)程:

  協(xié)程,又稱微線程,纖程,英文名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é)程只有一個(gè)線程執(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
<generator object simple_coroutine at 0x1085174f8>
>>> next(my_co)
coroutine start
>>> my_co.send(42)
coroutine recive: 42
Traceback (most recent call last):
 File "<input>", line 1, in <module>
StopIteration

其中x = yield就是精髓部分,意思是從客戶端獲取數(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 "<input>", line 1, in <module>
TypeError: 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ù)這一步通常稱為“預(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 "<input>", line 1, in <module>
StopIteration
>>> 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ù))不是線程切換,由程序自身控制,沒(méi)有切換線程的開(kāi)銷(xiāo)。所以與多線程相比,線程的數(shù)量越多,協(xié)程性能的優(yōu)勢(shì)越明顯。
  • 不需要多線程的鎖機(jī)制,因?yàn)橹挥幸粋€(gè)線程,也不存在同時(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ě)消息,一個(gè)線程取消息,通過(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í)行之后:

  1. 調(diào)用next(c)啟動(dòng)生成器;
  2. 進(jìn)入do_work,這是一個(gè)遞歸調(diào)用,其內(nèi)部將url傳遞給consumer,由consumer來(lái)發(fā)出請(qǐng)求,獲取到html信息,返回給produce,
  3. produce解析html,獲取url數(shù)據(jù),繼續(xù)生產(chǎn)url,
  4. 當(dāng)所有的url都在history_urls中,也就是說(shuō)我們已經(jīng)爬取了所有的url地址,結(jié)束遞歸調(diào)用
  5. 調(diào)用c.close(),關(guān)閉consumer,整個(gè)過(guò)程結(jié)束。

可以看到,我們的整個(gè)流程無(wú)鎖,由一個(gè)線程執(zhí)行,produce和consumer協(xié)作完成任務(wù),所以稱為“協(xié)程”,而非線程的搶占式多任務(wù)。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Python 正則表達(dá)式大全(推薦)

    Python 正則表達(dá)式大全(推薦)

    正則表達(dá)式是對(duì)字符串操作的一種邏輯公式,正則表達(dá)式是一種文本模式,該模式描述在搜索文本時(shí)要匹配的一個(gè)或多個(gè)字符串。本文重點(diǎn)給大家介紹Python 正則表達(dá)式大全,感興趣的朋友一起看看吧
    2021-11-11
  • Django中session登錄驗(yàn)證操作指南

    Django中session登錄驗(yàn)證操作指南

    本文介紹了如何使用Django中的session登錄驗(yàn)證來(lái)保護(hù)網(wǎng)站的安全性。在此過(guò)程中,我們首先介紹了Django的認(rèn)證架構(gòu)和基本概念,然后我們深入探討了如何使用session實(shí)現(xiàn)登錄驗(yàn)證功能。最后,我們解釋了如何創(chuàng)建一個(gè)Custom?User?Model,以及如何使用它來(lái)自定義用戶對(duì)象。
    2023-04-04
  • Python隨手筆記第一篇(2)之初識(shí)列表和元組

    Python隨手筆記第一篇(2)之初識(shí)列表和元組

    Python中,列表和元組是一種數(shù)據(jù)結(jié)構(gòu):序列,序列中的每個(gè)元素都被分配一個(gè)序號(hào),元素的位置,第一原元素的位置為0,因此類(lèi)推,序列是最基本的數(shù)據(jù)結(jié)構(gòu),本文給大家分享Python隨手筆記第一篇(2)之初識(shí)列表和元組,感興趣的朋友一起學(xué)習(xí)吧
    2016-01-01
  • Python模擬鋼琴鍵盤(pán)實(shí)現(xiàn)演奏數(shù)鴨子歌曲

    Python模擬鋼琴鍵盤(pán)實(shí)現(xiàn)演奏數(shù)鴨子歌曲

    前幾天上課老師給我們講了兩個(gè)模塊,然后利用這兩個(gè)模塊來(lái)模擬鋼琴鍵盤(pán)去簡(jiǎn)單地演奏《數(shù)鴨子》今天來(lái)分享給大家,感興趣的可以了解一下
    2022-11-11
  • python設(shè)置環(huán)境變量的作用和實(shí)例

    python設(shè)置環(huán)境變量的作用和實(shí)例

    在本篇文章里小編給各位整理了關(guān)于python設(shè)置環(huán)境變量的作用和實(shí)例內(nèi)容知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)參考下。
    2019-07-07
  • python中的deque雙向隊(duì)列詳解

    python中的deque雙向隊(duì)列詳解

    這篇文章主要介紹了python中的deque雙向隊(duì)列詳解,相比 list 實(shí)現(xiàn)的隊(duì)列,deque 擁有更低的時(shí)間和空間復(fù)雜度,list 實(shí)現(xiàn)在出隊(duì)(pop)和插入(insert)時(shí)的空間復(fù)雜度大約為O(n),需要的朋友可以參考下
    2023-09-09
  • pytorch加載自己的圖像數(shù)據(jù)集實(shí)例

    pytorch加載自己的圖像數(shù)據(jù)集實(shí)例

    這篇文章主要介紹了pytorch加載自己的圖像數(shù)據(jù)集實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-07-07
  • pycharm實(shí)現(xiàn)增加運(yùn)行時(shí)內(nèi)存

    pycharm實(shí)現(xiàn)增加運(yùn)行時(shí)內(nèi)存

    這篇文章主要介紹了pycharm實(shí)現(xiàn)增加運(yùn)行時(shí)內(nèi)存方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Python和Anaconda和Pycharm安裝教程圖文詳解

    Python和Anaconda和Pycharm安裝教程圖文詳解

    PyCharm是一種PythonIDE,帶有一整套可以幫助用戶在使用Python語(yǔ)言開(kāi)發(fā)時(shí)提高其效率的工具,這篇文章主要介紹了Python和Anaconda和Pycharm安裝教程,需要的朋友可以參考下
    2020-02-02
  • Python隨機(jī)采樣及概率分布(二)

    Python隨機(jī)采樣及概率分布(二)

    之前的《Python中的隨機(jī)采樣和概率分布我們介紹了Python中最簡(jiǎn)單的隨機(jī)采樣函數(shù)。接下來(lái)我們更進(jìn)一步,來(lái)看看如何從一個(gè)概率分布中采樣,我們以幾個(gè)機(jī)器學(xué)習(xí)中最常用的概率分布為例。
    2022-01-01

最新評(píng)論