日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
python協(xié)程函數(shù)切換 python協(xié)程返回值

python協(xié)程(4):asyncio

asyncio是官方提供的協(xié)程的類庫,從python3.4開始支持該模塊

創(chuàng)新互聯(lián)公司是一家專業(yè)提供徐水企業(yè)網(wǎng)站建設(shè),專注與成都做網(wǎng)站、成都網(wǎng)站制作、H5建站、小程序制作等業(yè)務(wù)。10年已為徐水眾多企業(yè)、政府機構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站建設(shè)公司優(yōu)惠進行中。

async awiat是python3.5中引入的關(guān)鍵字,使用async關(guān)鍵字可以將一個函數(shù)定義為協(xié)程函數(shù),使用awiat關(guān)鍵字可以在遇到IO的時候掛起當(dāng)前協(xié)程(也就是任務(wù)),去執(zhí)行其他協(xié)程。

await + 可等待的對象(協(xié)程對象、Future對象、Task對象 - IO等待)

注意:在python3.4中是通過asyncio裝飾器定義協(xié)程,在python3.8中已經(jīng)移除了asyncio裝飾器。

事件循環(huán),可以把他當(dāng)做是一個while循環(huán),這個while循環(huán)在周期性的運行并執(zhí)行一些協(xié)程(任務(wù)),在特定條件下終止循環(huán)。

loop = asyncio.get_event_loop():生成一個事件循環(huán)

loop.run_until_complete(任務(wù)):將任務(wù)放到事件循環(huán)

Tasks用于并發(fā)調(diào)度協(xié)程,通過asyncio.create_task(協(xié)程對象)的方式創(chuàng)建Task對象,這樣可以讓協(xié)程加入事件循環(huán)中等待被調(diào)度執(zhí)行。除了使用 asyncio.create_task() 函數(shù)以外,還可以用低層級的 loop.create_task() 或 ensure_future() 函數(shù)。不建議手動實例化 Task 對象。

本質(zhì)上是將協(xié)程對象封裝成task對象,并將協(xié)程立即加入事件循環(huán),同時追蹤協(xié)程的狀態(tài)。

注意:asyncio.create_task() 函數(shù)在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用 asyncio.ensure_future() 函數(shù)。

下面結(jié)合async awiat、事件循環(huán)和Task看一個示例

示例一:

*注意:python 3.7以后增加了asyncio.run(協(xié)程對象),效果等同于loop = asyncio.get_event_loop(),loop.run_until_complete(協(xié)程對象) *

示例二:

注意:asyncio.wait 源碼內(nèi)部會對列表中的每個協(xié)程執(zhí)行ensure_future從而封裝為Task對象,所以在和wait配合使用時task_list的值為[func(),func()] 也是可以的。

示例三:

python協(xié)程gevent怎么用

在學(xué)習(xí)gevent之前,你肯定要知道你學(xué)的這個東西是什么。

官方描述gevent

gevent is a coroutine-based Python networking library that uses greenlet to provide a high-level synchronous API on top of the libev event loop.

翻譯:gevent是一個基于協(xié)程的Python網(wǎng)絡(luò)庫。我們先理解這句,也是這次學(xué)習(xí)的重點——協(xié)程。

wiki描述協(xié)程

與子例程一樣,協(xié)程也是一種程序組件。相對子例程而言,協(xié)程更為一般和靈活,但在實踐中使用沒有子例程那樣廣泛。子例程的起始處是惟一的入口點,一旦退出即完成了子例程的執(zhí)行,子例程的一個實例只會返回一次;協(xié)程可以通過yield來調(diào)用其它協(xié)程。通過yield方式轉(zhuǎn)移執(zhí)行權(quán)的協(xié)程之間不是調(diào)用者與被調(diào)用者的關(guān)系,而是彼此對稱、平等的。協(xié)程允許多個入口點,可以在指定位置掛起和恢復(fù)執(zhí)行。

沒看懂?沒關(guān)系,我也沒看懂,不過算是有點線索:子例程。

子例程

過程有兩種,一種叫子例程(Subroutine),通常叫Sub;另一種叫函數(shù)(Function)。底層實現(xiàn)機制是一樣的,區(qū)別在于,Sub只執(zhí)行操作,沒有返回值;Function不但執(zhí)行操作,并且有返回值。用過VB的應(yīng)該會比較清楚這點。(原諒我用了百度百科)說到底子例程就是過程,我們一般叫它函數(shù)。

說到函數(shù),我就想吐槽了,不明白為什么要叫函數(shù)。很多時候我們寫一個函數(shù)是為了封裝、模塊化某個功能,它是一個功能、或者說是一個過程。因為它包含的是類似于流程圖那樣的具體邏輯,先怎樣做,然后怎樣做;如果遇到A情況則怎樣,如果遇到B情況又怎樣。個人覺得還是叫過程比較好,叫做函數(shù)就讓人很糾結(jié)了,難道因為回歸到底層還是計算問題,出于數(shù)學(xué)的角度把它稱為函數(shù)?這個略坑?。榱朔洗蠹业目谖?,我還是稱之為函數(shù)好了(其實我也習(xí)慣叫函數(shù)了%_

講到函數(shù),我們就往底層深入一點,看看下面的代碼:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

def a():

print "a start"

b()

print "a end"

def b():

print "b start"

c()

print "b end"

def c():

print "c start"

print "c end"

if __name__ == "__main__":

a()

a start

b start

c start

c end

b end

a end

對于這樣的結(jié)果大家肯定不會意外的。每當(dāng)函數(shù)被調(diào)用,就會在棧中開辟一個棧空間,調(diào)用結(jié)束后再回收該空間。

假設(shè)一個這樣的場景:有個講臺,每個人都可以上去發(fā)表言論,但是每次講臺只能站一個人。現(xiàn)在a在上面演講,當(dāng)他說到“大家好!”的時候,b有個緊急通知要告訴大家,所以a就先下來讓b講完通知,然后a再上講臺繼續(xù)演講。如果用函數(shù)的思想模擬這個問題,堆棧示意圖是這樣的:

大家會不會發(fā)現(xiàn)問題,就是b通知完a繼續(xù)演講都要重新開始。因為函數(shù)在重新調(diào)用的時候,它的局部變量是會被重置的,對于之前他說的那句“大家好”,他是不會記得的(可能a的記性不好)。那有沒有什么辦法可以不讓他重復(fù),而是在打斷之后繼續(xù)呢?很簡單,在他走下講臺之前記住當(dāng)前說過的話。表現(xiàn)在函數(shù)中就是在退出之前,保存該函數(shù)的局部變量,方便在重新進入該函數(shù)的時候,能夠從之前的局部變量開始繼續(xù)執(zhí)行。

升級版

如果你有一段代碼生產(chǎn)數(shù)據(jù),另外一段代碼消費數(shù)據(jù),哪個應(yīng)該是調(diào)用者,哪個應(yīng)該是被調(diào)用者?

例如:生產(chǎn)者 —— 消費者問題,先拋開進程、線程等實現(xiàn)方法。假設(shè)有兩個函數(shù)producer和consumer,當(dāng)緩沖區(qū)滿了,producer調(diào)用consumer,當(dāng)緩沖區(qū)空了,consumer調(diào)用producer,但是這樣的函數(shù)互相調(diào)用會出什么問題?

Python

1

2

3

4

5

6

7

8

def producer():

print "生產(chǎn)一個"

consumer()

def consumer():

print "消費一個"

producer()

producer生產(chǎn)一個,緩沖區(qū)滿了,consumer消費一個,緩沖區(qū)空了,producer生產(chǎn)一個,如此循環(huán)。會看到下面這樣的圖:

看起來好像不錯,感覺兩個函數(shù)協(xié)調(diào)運行的很好,很好的解決了生產(chǎn)者——消費者問題。如果真有這么好也就不會有協(xié)程的存在了,仔細(xì)分析會有兩個問題:

無限次數(shù)的函數(shù)嵌套調(diào)用,而沒有函數(shù)返回,會有什么樣的后果?

兩個函數(shù)貌似協(xié)調(diào)有序的工作,你來我往,但每次執(zhí)行的都是同一個函數(shù)實例嗎?

首先,上面的偽代碼示例是一個無限的函數(shù)嵌套調(diào)用,沒有函數(shù)返回來釋放棧,棧的空間不斷的在增長,直到溢出,程序崩潰。然后,看起來兩個函數(shù)協(xié)調(diào)有序,事實上操作的都不是同一個實例對象,不知道下面的圖能否看懂。

那什么東西有這樣的能力呢?我們很快就可以想到進程、線程,但是你真的想使用進程、線程如此重量級的東西在這么簡單的程序上嗎?野蠻的搶占式機制和笨重的上下文切換!

還有一種程序組件,那就是協(xié)程。它能保留上一次調(diào)用時的狀態(tài),每次重新進入該過程的時候,就相當(dāng)于回到上一次離開時所處邏輯流的位置。協(xié)程的起始處是第一個入口點,在協(xié)程里,返回點之后是接下來的入口點。協(xié)程的生命期完全由他們的使用的需要決定。每個協(xié)程在用yield命令向另一個協(xié)程交出控制時都盡可能做了更多的工作,放棄控制使得另一個協(xié)程從這個協(xié)程停止的地方開始,接下來的每次協(xié)程被調(diào)用時,都是從協(xié)程返回(或yield)的位置接著執(zhí)行。

從上面這些你就可以知道其實協(xié)程是模擬了多線程(或多進程)的操作,多線程在切換的時候都會有一個上下文切換,在退出的時候?qū)F(xiàn)場保存起來,等到下一次進入的時候從保存的現(xiàn)場開始,繼續(xù)執(zhí)行。

看下協(xié)程是怎樣實現(xiàn)的:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

import random

from time import sleep

from greenlet import greenlet

from Queue import Queue

queue = Queue(1)

@greenlet

def producer():

chars = ['a', 'b', 'c', 'd', 'e']

global queue

while True:

char = random.choice(chars)

queue.put(char)

print "Produced: ", char

sleep(1)

consumer.switch()

@greenlet

def consumer():

global queue

while True:

char = queue.get()

print "Consumed: ", char

sleep(1)

producer.switch()

if __name__ == "__main__":

producer.run()

consumer.run()

應(yīng)用場景

我們一直都在大談協(xié)程是什么樣一個東西,卻從沒有提起協(xié)程用來干嘛,這個其實大家分析一下就能夠知道。從上面的生產(chǎn)者——消費者問題應(yīng)該能看出,它分別有兩個任務(wù),假設(shè)交給兩個人去執(zhí)行,但每次只能允許一個人行動。當(dāng)緩沖區(qū)滿的時候,生產(chǎn)者是出于等待狀態(tài)的,這個時候可以將執(zhí)行任務(wù)的權(quán)利轉(zhuǎn)交給消費者,當(dāng)緩沖區(qū)空得時候,消費者是出于等待狀態(tài)的,這個時候可以將執(zhí)行任務(wù)的權(quán)利轉(zhuǎn)交給生產(chǎn)者,是不是很容易聯(lián)想到多任務(wù)切換?然后想到線程?最后想到高并發(fā)?

但同學(xué)們又會問,既然有了線程為什么還要協(xié)程呢?因為線程是系統(tǒng)級別的,在做切換的時候消耗是特別大的,具體為什么這么大等我研究好了再告訴你;同時線程的切換是由CPU決定的,可能你剛好執(zhí)行到一個地方的時候就要被迫終止,這個時候你需要用各種措施來保證你的數(shù)據(jù)不出錯,所以線程對于數(shù)據(jù)安全的操作是比較復(fù)雜的。而協(xié)程是用戶級別的切換,且切換是由自己控制,不受外力終止。

總結(jié)

協(xié)程其實模擬了人類活動的一種過程。例如:你準(zhǔn)備先寫文檔,然后修復(fù)bug。這時候接到電話說這個bug很嚴(yán)重,必須立即修復(fù)(可以看作CPU通知)。于是你暫停寫文檔,開始去填坑,終于你把坑填完了,你回來寫文檔,這個時候你肯定是接著之前寫的文檔繼續(xù),難道你要把之前寫的給刪了,重新寫?這就是協(xié)程。那如果是子例程呢?那你就必須重新寫了,因為退出之后,棧幀就會被彈出銷毀,再次調(diào)用就是開辟新的??臻g了。

總結(jié):協(xié)程就是用戶態(tài)下的線程,是人們在有了進程、線程之后仍覺得效率不夠,而追求的又一種高并發(fā)解決方案。為什么說是用戶態(tài),是因為操作系統(tǒng)并不知道它的存在,它是由程序員自己控制、互相協(xié)作的讓出控制權(quán)而不是像進程、線程那樣由操作系統(tǒng)調(diào)度決定是否讓出控制權(quán)。

Python異步編程4:協(xié)程函數(shù),協(xié)程對象,await關(guān)鍵字

協(xié)程函數(shù):async def?函數(shù)名。3.5+

協(xié)程對象:執(zhí)行協(xié)程函數(shù)()得到的協(xié)程對象。

3.5之后的寫法:

3.7之后的寫法:更簡便

await后面?跟?可等待的對象。(協(xié)程對象,F(xiàn)uture,Task對象?約等于IO等待)

await實例2:串行執(zhí)行。 一個協(xié)程函數(shù)里面可以支持多個await ,雖然會串行,但是如果有其他協(xié)程函數(shù),任務(wù)列表也在執(zhí)行,依然會切換。只是案例中的main對應(yīng)執(zhí)行的others1和others2串行 。 await會等待對象的值得到之后才繼續(xù)往下走。

python 中的協(xié)程是怎么實現(xiàn)多任務(wù)的?

協(xié)程也稱為微線程,是在一個線程中,通過不斷的切換任務(wù)函數(shù)實現(xiàn)了多任務(wù)的效果。

協(xié)程在python實現(xiàn)的原理主要是通過yield這個關(guān)鍵字實現(xiàn)

但是真正在開發(fā)時,可以不需要自己實現(xiàn),可以通過很多成熟的第三方模塊來實現(xiàn)協(xié)程,比如greenlet,gevent等模塊。多線程的課程我記得是在黑馬程序員里面找的,一套,還有資料。

python中多進程+協(xié)程的使用以及為什么要用它

python里推薦用多進程而不是多線程,但是多進程也有其自己的限制:相比線程更加笨重、切換耗時更長,并且在python的多進程下,進程數(shù)量不推薦超過CPU核心數(shù)(一個進程只有一個GIL,所以一個進程只能跑滿一個CPU),因為一個進程占用一個CPU時能充分利用機器的性能,但是進程多了就會出現(xiàn)頻繁的進程切換,反而得不償失。

不過特殊情況(特指IO密集型任務(wù))下,多線程是比多進程好用的。

舉個例子:給你200W條url,需要你把每個url對應(yīng)的頁面抓取保存起來,這種時候,單單使用多進程,效果肯定是很差的。為什么呢?

例如每次請求的等待時間是2秒,那么如下(忽略cpu計算時間):

1、單進程+單線程:需要2秒*200W=400W秒==1111.11個小時==46.3天,這個速度明顯是不能接受的

2、單進程+多線程:例如我們在這個進程中開了10個多線程,比1中能夠提升10倍速度,也就是大約4.63天能夠完成200W條抓取,請注意,這里的實際執(zhí)行是:線程1遇見了阻塞,CPU切換到線程2去執(zhí)行,遇見阻塞又切換到線程3等等,10個線程都阻塞后,這個進程就阻塞了,而直到某個線程阻塞完成后,這個進程才能繼續(xù)執(zhí)行,所以速度上提升大約能到10倍(這里忽略了線程切換帶來的開銷,實際上的提升應(yīng)該是不能達到10倍的),但是需要考慮的是線程的切換也是有開銷的,所以不能無限的啟動多線程(開200W個線程肯定是不靠譜的)

3、多進程+多線程:這里就厲害了,一般來說也有很多人用這個方法,多進程下,每個進程都能占一個cpu,而多線程從一定程度上繞過了阻塞的等待,所以比單進程下的多線程又更好使了,例如我們開10個進程,每個進程里開20W個線程,執(zhí)行的速度理論上是比單進程開200W個線程快10倍以上的(為什么是10倍以上而不是10倍,主要是cpu切換200W個線程的消耗肯定比切換20W個進程大得多,考慮到這部分開銷,所以是10倍以上)。

還有更好的方法嗎?答案是肯定的,它就是:

4、協(xié)程,使用它之前我們先講講what/why/how(它是什么/為什么用它/怎么使用它)

what:

協(xié)程是一種用戶級的輕量級線程。協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復(fù)先前保存的寄存器上下文和棧。因此:

協(xié)程能保留上一次調(diào)用時的狀態(tài)(即所有局部狀態(tài)的一個特定組合),每次過程重入時,就相當(dāng)于進入上一次調(diào)用的狀態(tài),換種說法:進入上一次離開時所處邏輯流的位置。

在并發(fā)編程中,協(xié)程與線程類似,每個協(xié)程表示一個執(zhí)行單元,有自己的本地數(shù)據(jù),與其它協(xié)程共享全局?jǐn)?shù)據(jù)和其它資源。

why:

目前主流語言基本上都選擇了多線程作為并發(fā)設(shè)施,與線程相關(guān)的概念是搶占式多任務(wù)(Preemptive multitasking),而與協(xié)程相關(guān)的是協(xié)作式多任務(wù)。

不管是進程還是線程,每次阻塞、切換都需要陷入系統(tǒng)調(diào)用(system call),先讓CPU跑操作系統(tǒng)的調(diào)度程序,然后再由調(diào)度程序決定該跑哪一個進程(線程)。而且由于搶占式調(diào)度執(zhí)行順序無法確定的特點,使用線程時需要非常小心地處理同步問題,而協(xié)程完全不存在這個問題(事件驅(qū)動和異步程序也有同樣的優(yōu)點)。

因為協(xié)程是用戶自己來編寫調(diào)度邏輯的,對CPU來說,協(xié)程其實是單線程,所以CPU不用去考慮怎么調(diào)度、切換上下文,這就省去了CPU的切換開銷,所以協(xié)程在一定程度上又好于多線程。

how:

python里面怎么使用協(xié)程?答案是使用gevent,使用方法:看這里

使用協(xié)程,可以不受線程開銷的限制,我嘗試過一次把20W條url放在單進程的協(xié)程里執(zhí)行,完全沒問題。

所以最推薦的方法,是多進程+協(xié)程(可以看作是每個進程里都是單線程,而這個單線程是協(xié)程化的)

多進程+協(xié)程下,避開了CPU切換的開銷,又能把多個CPU充分利用起來,這種方式對于數(shù)據(jù)量較大的爬蟲還有文件讀寫之類的效率提升是巨大的。

python里協(xié)程事件循環(huán)里怎么樣調(diào)用非協(xié)程函數(shù)

為了管理協(xié)程和I/O的回調(diào)函數(shù),asyncio庫的事件循環(huán)也能基于定時的方式調(diào)用普通的函數(shù),使用call_soon()函數(shù),例子如下:

import?asyncio??

import?functools??

def?callback(arg,?*,?kwarg='default'):??

print('callback?invoked?with?{}?and?{}'.format(arg,?kwarg))??

async?def?main(loop):??

print('registering?callbacks')??

loop.call_soon(callback,?1)??

wrapped?=?functools.partial(callback,?kwarg='not?default')??

loop.call_soon(wrapped,?2)??

await?asyncio.sleep(0.1)??

event_loop?=?asyncio.get_event_loop()??

try:??

print('entering?event?loop')??

event_loop.run_until_complete(main(event_loop))??

finally:??

print('closing?event?loop')??

event_loop.close()

結(jié)果輸出如下:

entering event loop

registering callbacks

callback invoked with 1 and default

callback invoked with 2 and not default

closing event loop


新聞標(biāo)題:python協(xié)程函數(shù)切換 python協(xié)程返回值
當(dāng)前鏈接:http://www.dlmjj.cn/article/doddegp.html