国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Python學(xué)習(xí)之路-多任務(wù):協(xié)程

這篇具有很好參考價值的文章主要介紹了Python學(xué)習(xí)之路-多任務(wù):協(xié)程。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

Python學(xué)習(xí)之路-多任務(wù):協(xié)程

簡介

協(xié)程,又稱微線程,纖程。英文名Coroutine。協(xié)程是python個中另外一種實現(xiàn)多任務(wù)的方式,只不過比線程更小占用更小執(zhí)行單元(理解為需要的資源)。 為啥說它是一個執(zhí)行單元,因為它自帶CPU上下文。這樣只要在合適的時機(jī), 我們可以把一個協(xié)程 切換到另一個協(xié)程。 只要這個過程中保存或恢復(fù) CPU上下文那么程序還是可以運(yùn)行的。

通俗的理解:在一個線程中的某個函數(shù),可以在任何地方保存當(dāng)前函數(shù)的一些臨時變量等信息,然后切換到另外一個函數(shù)中執(zhí)行,注意不是通過調(diào)用函數(shù)的方式做到的,并且切換的次數(shù)以及什么時候再切換到原來的函數(shù)都由開發(fā)者自己確定

協(xié)程和線程差異

在實現(xiàn)多任務(wù)時, 線程切換從系統(tǒng)層面遠(yuǎn)不止保存和恢復(fù) CPU上下文這么簡單。 操作系統(tǒng)為了程序運(yùn)行的高效性每個線程都有自己緩存Cache等等數(shù)據(jù),操作系統(tǒng)還會幫你做這些數(shù)據(jù)的恢復(fù)操作。 所以線程的切換非常耗性能。但是協(xié)程的切換只是單純的操作CPU的上下文,所以一秒鐘切換個上百萬次系統(tǒng)都抗的住。

迭代

迭代是訪問集合元素的一種方式。使用for…in…的循環(huán)語法從其中依次拿到數(shù)據(jù)進(jìn)行使用,我們把這樣的過程稱為遍歷,也叫迭代。

迭代器

迭代器是一個可以記住遍歷的位置的對象。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會后退。

可迭代對象

把可以通過for…in…這類語句迭代讀取一條數(shù)據(jù)供我們使用的對象稱之為可迭代對象(Iterable)??梢允褂?isinstance() 判斷一個對象是否是可迭代對象 Iterable 對象。

可迭代對象的本質(zhì)

我們分析對可迭代對象進(jìn)行迭代使用的過程,發(fā)現(xiàn)每迭代一次(即在for…in…中每循環(huán)一次)都會返回對象中的下一條數(shù)據(jù),一直向后讀取數(shù)據(jù)直到迭代了所有數(shù)據(jù)后結(jié)束。那么,在這個過程中就應(yīng)該有一個“人”去記錄每次訪問到了第幾條數(shù)據(jù),以便每次迭代都可以返回下一條數(shù)據(jù)。我們把這個能幫助我們進(jìn)行數(shù)據(jù)迭代的“人”稱為迭代器(Iterator)。

可迭代對象的本質(zhì)就是可以向我們提供一個這樣的中間“人”即迭代器幫助我們對其進(jìn)行迭代遍歷使用。

可迭代對象通過__iter__方法向我們提供一個迭代器,我們在迭代一個可迭代對象的時候,實際上就是先獲取該對象提供的一個迭代器,然后通過這個迭代器來依次獲取對象中的每一個數(shù)據(jù).

那么也就是說,一個具備了__iter__方法的對象,就是一個可迭代對象。

{{< admonition warning “注意” true >}}

可以通過iter()函數(shù)獲取這些可迭代對象的迭代器(iter()函數(shù)實際上就是調(diào)用了可迭代對象的__iter__方法),然后我們可以對獲取到的迭代器不斷使用next()函數(shù)來獲取下一條數(shù)據(jù)(使用next()函數(shù)的時候,調(diào)用的就是迭代器對象的__next__方法)。當(dāng)我們已經(jīng)迭代完最后一個數(shù)據(jù)之后,再次調(diào)用next()函數(shù)會拋出StopIteration的異常,來告訴我們所有數(shù)據(jù)都已迭代完成,不用再執(zhí)行next()函數(shù)了。

{{< /admonition >}}

for…in…循環(huán)的本質(zhì)

for item in Iterable 循環(huán)的本質(zhì)就是先通過iter()函數(shù)獲取可迭代對象Iterable的迭代器,然后對獲取到的迭代器不斷調(diào)用next()方法來獲取下一個值并將其賦值給item,當(dāng)遇到StopIteration的異常后循環(huán)結(jié)束。

生成器

簡介

生成器是一類特殊的迭代器。利用迭代器,我們可以在每次迭代獲取數(shù)據(jù)(通過next()方法)時按照特定的規(guī)律進(jìn)行生成。但是我們在實現(xiàn)一個迭代器時,關(guān)于當(dāng)前迭代到的狀態(tài)需要我們自己記錄,進(jìn)而才能根據(jù)當(dāng)前狀態(tài)生成下一個數(shù)據(jù)。為了達(dá)到記錄當(dāng)前狀態(tài),并配合next()函數(shù)進(jìn)行迭代使用,我們可以采用更簡便的語法,即生成器(generator)

創(chuàng)建生成器方法

  • 只要把一個列表生成式的 [ ] 改成 ( ),如下

    In [1]: A = [ x*2 for x in range(5)]
    
    In [2]: A
    Out[2]: [0, 2, 4, 6, 8]
    
    In [3]: B = ( x*2 for x in range(5))
    
    In [4]: B
    Out[4]: <generator object <genexpr> at 0x7f626c132db0>
    

    創(chuàng)建 L 和 G 的區(qū)別僅在于最外層的 [ ] 和 ( ) , L 是一個列表,而 G 是一個生成器。我們可以直接打印出列表L的每一個元素,而對于生成器G,我們可以按照迭代器的使用方法來使用,即可以通過next()函數(shù)、for循環(huán)、list()等方法使用。

    In [5]: next(G)
    Out[5]: 0
    
    In [6]: next(G)
    Out[6]: 2
    
    In [7]: next(G)
    Out[7]: 4
    
    In [8]: next(G)
    Out[8]: 6
    
    In [9]: next(G)
    Out[9]: 8
    
    In [10]: next(G)
    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-24-380e167d6934> in <module>()
    ----> 1 next(G)
    
    StopIteration:
      
    In [11]:
    In [12]: G = ( x*2 for x in range(5))
    
    In [13]: for x in G:
       ....:     print(x)
       ....:     
    0
    2
    4
    6
    8
    
    In [14]:
    
  • 將原本在迭代器__next__方法中實現(xiàn)的基本邏輯放到一個函數(shù)中來實現(xiàn),但是將每次迭代返回數(shù)值的return換成了yield,此時新定義的函數(shù)便不再是函數(shù),而是一個生成器了。簡單來說:只要在def中有yield關(guān)鍵字的 就稱為 生成器。用生成器實現(xiàn)斐波那契數(shù)列

    In [15]: def fib(n):
       ....:     current = 0
       ....:     num1, num2 = 0, 1
       ....:     while current < n:
       ....:         num = num1
       ....:         num1, num2 = num2, num1+num2
       ....:         current += 1
       ....:         yield num
       ....:     return 'done'
       ....:
    
    In [16]: F = fib(5)
    
    In [17]: next(F)
    Out[17]: 1
    
    In [18]: next(F)
    Out[18]: 1
    
    In [19]: next(F)
    Out[19]: 2
    
    In [20]: next(F)
    Out[20]: 3
    
    In [21]: next(F)
    Out[21]: 5
    
    In [22]: next(F)
    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-22-8c2b02b4361a> in <module>()
    ----> 1 next(F)
    
    StopIteration: done
    

    此時按照調(diào)用函數(shù)的方式( 案例中為F = fib(5) )使用生成器就不再是執(zhí)行函數(shù)體了,而是會返回一個生成器對象( 案例中為F ),然后就可以按照使用迭代器的方式來使用生成器了。

    In [23]: for n in fib(5):
       ....:     print(n)
       ....:     
    1
    1
    2
    3
    5
    

    但是用for循環(huán)調(diào)用generator時,發(fā)現(xiàn)拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:

    In [25]: g = fib(5)
    
    In [26]: while True:
       ....:     try:
       ....:         x = next(g)
       ....:         print("value:%d"%x)      
       ....:     except StopIteration as e:
       ....:         print("生成器返回值:%s"%e.value)
       ....:         break
       ....:     
    value:1
    value:1
    value:2
    value:3
    value:5
    生成器返回值:done
    

使用send喚醒

我們除了可以使用next()函數(shù)來喚醒生成器繼續(xù)執(zhí)行外,還可以使用send()函數(shù)來喚醒執(zhí)行。使用send()函數(shù)的一個好處是可以在喚醒的同時向斷點處傳入一個附加數(shù)據(jù)。

例子:執(zhí)行到y(tǒng)ield時,gen函數(shù)作用暫時保存,返回i的值; temp接收下次c.send(“python”),send發(fā)送過來的值,c.next()等價c.send(None)

In [27]: def gen():
   ....:     i = 0
   ....:     while i<5:
   ....:         temp = yield i
   ....:         print(temp)
   ....:         i+=1
   ....:

使用send

In [28]: f = gen()

In [29]: next(f)
Out[29]: 0

In [30]: f.send('haha')
haha
Out[30]: 1

In [31]: next(f)
None
Out[31]: 2

In [32]: f.send('haha')
haha
Out[32]: 3

使用next函數(shù)

In [33]: f = gen()

In [34]: next(f)
Out[34]: 0

In [35]: next(f)
None
Out[35]: 1

In [36]: next(f)
None
Out[36]: 2

In [37]: next(f)
None
Out[37]: 3

In [38]: next(f)
None
Out[38]: 4

In [39]: next(f)
None
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-17-468f0afdf1b9> in <module>()
----> 1 next(f)

StopIteration:

使用__next__()方法(不常使用)

In [40]: f = gen()

In [41]: f.__next__()
Out[41]: 0

In [42]: f.__next__()
None
Out[42]: 1

In [43]: f.__next__()
None
Out[43]: 2

In [44]: f.__next__()
None
Out[44]: 3

In [45]: f.__next__()
None
Out[45]: 4

In [46]: f.__next__()
None
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-24-39ec527346a9> in <module>()
----> 1 f.__next__()

StopIteration:

簡單實現(xiàn)協(xié)程

import time

def work1():
    while True:
        print("----work1---")
        yield
        time.sleep(0.5)

def work2():
    while True:
        print("----work2---")
        yield
        time.sleep(0.5)

def main():
    w1 = work1()
    w2 = work2()
    while True:
        next(w1)
        next(w2)

if __name__ == "__main__":
    main()

運(yùn)行結(jié)果:

----work1---
----work2---
----work1---
----work2---
----work1---
----work2---
----work1---
----work2---
----work1---
----work2---
----work1---
----work2---
...省略...

greenlet

為了更好使用協(xié)程來完成多任務(wù),python中的greenlet模塊對其封裝,從而使得切換任務(wù)變的更加簡單

安裝

使用如下命令安裝greenlet模塊:

sudo pip3 install greenlet

簡單實現(xiàn)

from greenlet import greenlet
import time

def test1():
    while True:
        print "---A--"
        gr2.switch()
        time.sleep(0.5)

def test2():
    while True:
        print "---B--"
        gr1.switch()
        time.sleep(0.5)

gr1 = greenlet(test1)
gr2 = greenlet(test2)

#切換到gr1中運(yùn)行
gr1.switch()

運(yùn)行效果:

---A--
---B--
---A--
---B--
---A--
---B--
---A--
---B--
...省略...

gevent

greenlet已經(jīng)實現(xiàn)了協(xié)程,但是這個還的人工切換,是不是覺得太麻煩了,不要捉急,python還有一個比greenlet更強(qiáng)大的并且能夠自動切換任務(wù)的模塊gevent

其原理是當(dāng)一個greenlet遇到IO(指的是input output 輸入輸出,比如網(wǎng)絡(luò)、文件操作等)操作時,比如訪問網(wǎng)絡(luò),就自動切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r候切換回來繼續(xù)執(zhí)行。

由于IO操作非常耗時,經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動切換協(xié)程,就保證總有g(shù)reenlet在運(yùn)行,而不是等待IO

安裝

使用如下命令安裝gevent模塊:

pip3 install gevent

簡單實現(xiàn)

import gevent

def f(n):
    for i in range(n):
        print(gevent.getcurrent(), i)

g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()

運(yùn)行結(jié)果

<Greenlet at 0x10e49f550: f(5)> 0
<Greenlet at 0x10e49f550: f(5)> 1
<Greenlet at 0x10e49f550: f(5)> 2
<Greenlet at 0x10e49f550: f(5)> 3
<Greenlet at 0x10e49f550: f(5)> 4
<Greenlet at 0x10e49f910: f(5)> 0
<Greenlet at 0x10e49f910: f(5)> 1
<Greenlet at 0x10e49f910: f(5)> 2
<Greenlet at 0x10e49f910: f(5)> 3
<Greenlet at 0x10e49f910: f(5)> 4
<Greenlet at 0x10e49f4b0: f(5)> 0
<Greenlet at 0x10e49f4b0: f(5)> 1
<Greenlet at 0x10e49f4b0: f(5)> 2
<Greenlet at 0x10e49f4b0: f(5)> 3
<Greenlet at 0x10e49f4b0: f(5)> 4

可以看到,3個greenlet是依次運(yùn)行而不是交替運(yùn)行

切換執(zhí)行

import gevent

def f(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        #用來模擬一個耗時操作,注意不是time模塊中的sleep
        gevent.sleep(1)

g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()

運(yùn)行結(jié)果

<Greenlet at 0x7fa70ffa1c30: f(5)> 0
<Greenlet at 0x7fa70ffa1870: f(5)> 0
<Greenlet at 0x7fa70ffa1eb0: f(5)> 0
<Greenlet at 0x7fa70ffa1c30: f(5)> 1
<Greenlet at 0x7fa70ffa1870: f(5)> 1
<Greenlet at 0x7fa70ffa1eb0: f(5)> 1
<Greenlet at 0x7fa70ffa1c30: f(5)> 2
<Greenlet at 0x7fa70ffa1870: f(5)> 2
<Greenlet at 0x7fa70ffa1eb0: f(5)> 2
<Greenlet at 0x7fa70ffa1c30: f(5)> 3
<Greenlet at 0x7fa70ffa1870: f(5)> 3
<Greenlet at 0x7fa70ffa1eb0: f(5)> 3
<Greenlet at 0x7fa70ffa1c30: f(5)> 4
<Greenlet at 0x7fa70ffa1870: f(5)> 4
<Greenlet at 0x7fa70ffa1eb0: f(5)> 4

給程序打補(bǔ)丁

from gevent import monkey
import gevent
import random
import time

def coroutine_work(coroutine_name):
    for i in range(10):
        print(coroutine_name, i)
        time.sleep(random.random())

gevent.joinall([
        gevent.spawn(coroutine_work, "work1"),
        gevent.spawn(coroutine_work, "work2")
])

運(yùn)行結(jié)果

work1 0
work1 1
work1 2
work1 3
work1 4
work1 5
work1 6
work1 7
work1 8
work1 9
work2 0
work2 1
work2 2
work2 3
work2 4
work2 5
work2 6
work2 7
work2 8
work2 9

monkey

from gevent import monkey
import gevent
import random
import time

# 有耗時操作時需要
monkey.patch_all()  # 將程序中用到的耗時操作的代碼,換為gevent中自己實現(xiàn)的模塊

def coroutine_work(coroutine_name):
    for i in range(10):
        print(coroutine_name, i)
        time.sleep(random.random())

gevent.joinall([
        gevent.spawn(coroutine_work, "work1"),
        gevent.spawn(coroutine_work, "work2")
])

運(yùn)行結(jié)果文章來源地址http://www.zghlxwxcb.cn/news/detail-797584.html

work1 0
work2 0
work1 1
work1 2
work1 3
work2 1
work1 4
work2 2
work1 5
work2 3
work1 6
work1 7
work1 8
work2 4
work2 5
work1 9
work2 6
work2 7
work2 8
work2 9

進(jìn)程、線程、協(xié)程對比

  1. 進(jìn)程是資源分配的單位
  2. 線程是操作系統(tǒng)調(diào)度的單位
  3. 進(jìn)程切換需要的資源很最大,效率很低
  4. 線程切換需要的資源一般,效率一般(當(dāng)然了在不考慮GIL的情況下)
  5. 協(xié)程切換任務(wù)資源很小,效率高
  6. 多進(jìn)程、多線程根據(jù)cpu核數(shù)不一樣可能是并行的,但是協(xié)程是在一個線程中 所以是并發(fā)

到了這里,關(guān)于Python學(xué)習(xí)之路-多任務(wù):協(xié)程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包