国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

python---協程

lolomaco / 2588人閱讀

摘要:隨著我們對于效率的追求不斷提高,基于單線程來實現并發又成為一個新的課題,即只用一個主線程很明顯可利用的只有一個情況下實現并發。作為的補充可以檢測操作,在遇到操作的情況下才發生切換協程介紹協程是單線程下的并發,又稱微線程,纖程。

引子

之前我們學習了線程、進程的概念,了解了在操作系統中進程是資源分配的最小單位,線程是CPU調度的最小單位。按道理來說我們已經算是把cpu的利用率提高很多了。但是我們知道無論是創建多進程還是創建多線程來解決問題,都要消耗一定的時間來創建進程、創建線程、以及管理他們之間的切換。
  隨著我們對于效率的追求不斷提高,基于單線程來實現并發又成為一個新的課題,即只用一個主線程(很明顯可利用的cpu只有一個)情況下實現并發。這樣就可以節省創建線進程所消耗的時間。
  為此我們需要先回顧下并發的本質:切換+保存狀態
  cpu正在運行一個任務,會在兩種情況下切走去執行其他的任務(切換由操作系統強制控制),一種情況是該任務發生了阻塞,另外一種情況是該任務計算的時間過長

在介紹進程理論時,提及進程的三種執行狀態,而線程才是執行單位,所以也可以將上圖理解為線程的三種狀態
一:其中第二種情況并不能提升效率,只是為了讓cpu能夠雨露均沾,實現看起來所有任務都被“同時”執行的效果,如果多個任務都是純計算的,這種切換反而會降低效率。
  為此我們可以基于yield來驗證。yield本身就是一種在單線程下可以保存任務運行狀態的方法:

1 yiled可以保存狀態,yield的狀態保存與操作系統的保存線程狀態很像,但是yield是代碼級別控制的,更輕量級
2 send可以把一個函數的結果傳給另外一個函數,以此實現單線程內程序之間的切換
#串行執行
import time
def consumer(res):
    """任務1:接收數據,處理數據"""
    pass

def producer():
    """任務2:生產數據"""
    res=[]
    for i in range(10000000):
        res.append(i)
    return res

start=time.time()
#串行執行
res=producer()
consumer(res) #寫成consumer(producer())會降低執行效率
stop=time.time()
print(stop-start) #1.874000072479248


#基于yield并發執行
import time
def consumer():
    """任務1:接收數據,處理數據"""
    while True:
        print("this is consumer")
        x=yield

def producer():
    """任務2:生產數據"""
    g=consumer()
    next(g)
    for i in range(10):
        print("this is prodecer")
        g.send(i)

start=time.time()
#基于yield保存狀態,實現兩個任務直接來回切換,即并發的效果
#PS:如果每個任務中都加上打印,那么明顯地看到兩個任務的打印是你一次我一次,即并發執行的.
producer()

stop=time.time()
print(stop-start) #2.0272178649902344

二:第一種情況的切換。在任務一遇到io情況下,切到任務二去執行,這樣就可以利用任務一阻塞的時間完成任務二的計算,效率的提升就在于此。

import time
def consumer():
    """任務1:接收數據,處理數據"""
    while True:
        x=yield

def producer():
    """任務2:生產數據"""
    g=consumer()
    next(g)
    for i in range(10000000):
        g.send(i)
        time.sleep(2)

start=time.time()
producer() #并發執行,但是任務producer遇到io就會阻塞住,并不會切到該線程內的其他任務去執行

stop=time.time()
print(stop-start)

對于單線程下,我們不可避免程序中出現io操作,但如果我們能在自己的程序中(即用戶程序級別,而非操作系統級別)控制單線程下的多個任務能在一個任務遇到io阻塞時就切換到另外一個任務去計算,這樣就保證了該線程能夠最大限度地處于就緒態,即隨時都可以被cpu執行的狀態,相當于我們在用戶程序級別將自己的io操作最大限度地隱藏起來,從而可以迷惑操作系統,讓其看到:該線程好像是一直在計算,io比較少,從而更多的將cpu的執行權限分配給我們的線程。

協程的本質就是在單線程下,由用戶自己控制一個任務遇到io阻塞了就切換另外一個任務去執行,以此來提升效率。為了實現它,我們需要找尋一種可以同時滿足以下條件的解決方案:

1. 可以控制多個任務之間的切換,切換之前將任務的狀態保存下來,以便重新運行時,可以基于暫停的位置繼續執行。
2. 作為1的補充:可以檢測io操作,在遇到io操作的情況下才發生切換

協程介紹

協程:是單線程下的并發,又稱微線程,纖程。英文名Coroutine。一句話說明什么是線程:協程是一種用戶態的輕量級線程,即協程是由用戶程序自己控制調度的。
需要強調的是:

1. python的線程屬于內核級別的,即由操作系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其他線程運行)
2. 單線程內開啟協程,一旦遇到io,就會從應用程序級別(而非操作系統)控制切換,以此來提升效率(!!!非io操作的切換與效率無關)

對比操作系統控制線程的切換,用戶在單線程內控制協程的切換
優點如下:

1. 協程的切換開銷更小,屬于程序級別的切換,操作系統完全感知不到,因而更加輕量級
2. 單線程內就可以實現并發的效果,最大限度地利用cpu

缺點如下:

1. 協程的本質是單線程下,無法利用多核,可以是一個程序開啟多個進程,每個進程內開啟多個線程,每個線程內開啟協程
2. 協程指的是單個線程,因而一旦協程出現阻塞,將會阻塞整個線程

總結協程特點:

1.必須在只有一個單線程里實現并發
2.修改共享數據不需加鎖
3.用戶程序里自己保存多個控制流的上下文棧
4.一個協程遇到IO操作自動切換到其它協程(如何實現檢測IO,yield、greenlet都無法實現,就用到了gevent模塊(select機制))   
Gevent模塊

安裝:pip3 install gevent
Gevent 是一個第三方庫,可以輕松通過gevent實現并發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。
用法介紹

g1=gevent.spawn(func,1,,2,3,x=4,y=5)創建一個協程對象g1,spawn括號內第一個參數是函數名,
如eat,后面可以有多個參數,可以是位置實參或關鍵字實參,都是傳給函數eat的

g2=gevent.spawn(func2)

g1.join() #等待g1結束

g2.join() #等待g2結束

#或者上述兩步合作一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值

例:遇到io主動切換

import gevent
def eat(name):
    print("%s eat 1" %name)
    gevent.sleep(2)
    print("%s eat 2" %name)

def play(name):
    print("%s play 1" %name)
    gevent.sleep(1)
    print("%s play 2" %name)


g1=gevent.spawn(eat,"egon")
g2=gevent.spawn(play,"egon")
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print("主")

上例gevent.sleep(2)模擬的是gevent可以識別的io阻塞,而time.sleep(2)或其他的阻塞,gevent是不能直接識別的需要用下面一行代碼,打補丁,就可以識別了

from gevent import monkey;monkey.patch_all()必須放到被打補丁者的前面,如time,socket模塊之前

或者我們干脆記憶成:要用gevent,需要將from gevent import monkey;monkey.patch_all()放到文件的開頭

from gevent import monkey;monkey.patch_all()

import gevent
import time
def eat():
    print("eat food 1")
    time.sleep(2)
    print("eat food 2")

def play():
    print("play 1")
    time.sleep(1)
    print("play 2")

g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])
print("主")

我們可以用threading.current_thread().getName()來查看每個g1和g2,查看的結果為DummyThread-n,即假線程

from gevent import monkey;monkey.patch_all()
import threading
import gevent
import time
def eat():
    print(threading.current_thread().getName())
    print("eat food 1")
    time.sleep(2)
    print("eat food 2")

def play():
    print(threading.current_thread().getName())
    print("play 1")
    time.sleep(1)
    print("play 2")

g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])
print("主")
Gevent之同步與異步
from gevent import spawn, joinall, monkey;monkey.patch_all()
import time
def task(pid):
    """
    Some non-deterministic task
    """
    time.sleep(0.5)
    print("Task %s done" % pid)


def synchronous():  # 同步
    for i in range(10):
        task(i)
def asynchronous():  # 異步
    g_l = [spawn(task, i) for i in range(10)]
    joinall(g_l)
    print("DONE")
if __name__ == "__main__":
    print("Synchronous:")
    synchronous()
    print("Asynchronous:")
    asynchronous()
#  上面程序的重要部分是將task函數封裝到Greenlet內部線程的gevent.spawn。
#  初始化的greenlet列表存放在數組threads中,此數組被傳給gevent.joinall 函數,
#  后者阻塞當前流程,并執行所有給定的greenlet任務。執行流程只會在 所有greenlet執行完后才會繼續向下走。
協程socket
from gevent import monkey;monkey.patch_all()
import socket
import gevent

def talk(conn):
    while True:
        conn.send(b"hello")
        print(conn.recv(1024))

sk = socket.socket()
sk.bind(("127.0.0.1",9090))
sk.listen()

while True:
    conn,addr = sk.accept()
    g = gevent.spawn(talk,conn)

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/42185.html

相關文章

  • Python協程(真才實學,想學的進來)

    摘要:所以與多線程相比,線程的數量越多,協程性能的優勢越明顯。值得一提的是,在此過程中,只有一個線程在執行,因此這與多線程的概念是不一樣的。 真正有知識的人的成長過程,就像麥穗的成長過程:麥穗空的時候,麥子長得很快,麥穗驕傲地高高昂起,但是,麥穗成熟飽滿時,它們開始謙虛,垂下麥芒。 ——蒙田《蒙田隨筆全集》 上篇論述了關于python多線程是否是雞肋的問題,得到了一些網友的認可,當然也有...

    lykops 評論0 收藏0
  • 異步等待的 Python 協程

    摘要:輔之以事件循環,協程可用于異步處理,尤其是在中。當前支持的協程基于增強型生成器,于版本開始采用。新的特性中,異步還有兩種新用途異步內容管理器和迭代器。 現在 Python 已經支持用協程進行異步處理。但最近有建議稱添加協程以全面完善 Python 的語言結構,而不是像現在這樣把他們作為生成器的一個類型。此外,兩個新的關鍵字———異步(async)和等待(await),都該添加到 Pyt...

    NicolasHe 評論0 收藏0
  • python協程與golang協程的區別

    摘要:進程線程和協程進程的定義進程,是計算機中已運行程序的實體。協程和線程的關系協程是在語言層面實現對線程的調度,避免了內核級別的上下文消耗。和都引入了消息調度系統模型,來避免鎖的影響和進程線程開銷大的問題。 進程、線程和協程 進程的定義: 進程,是計算機中已運行程序的實體。程序本身只是指令、數據及其組織形式的描述,進程才是程序的真正運行實例。 線程的定義: 操作系統能夠進行運算調度的最小單...

    csRyan 評論0 收藏0
  • python基礎教程:異步IO 之編程例子

    摘要:創建第一個協程推薦使用語法來聲明協程,來編寫異步應用程序。協程兩個緊密相關的概念是協程函數通過定義的函數協程對象調用協程函數返回的對象。它是一個低層級的可等待對象,表示一個異步操作的最終結果。 我們講以Python 3.7 上的asyncio為例講解如何使用Python的異步IO。 showImg(https://segmentfault.com/img/remote/14600000...

    wangxinarhat 評論0 收藏0
  • python大佬養成計劃----協程

    摘要:協程,又稱微線程,纖程。最大的優勢就是協程極高的執行效率。生產者產出第條數據返回更新值更新消費者正在調用第條數據查看當前進行的線程函數中有,返回值為生成器庫實現協程通過提供了對協程的基本支持,但是不完全。 協程,又稱微線程,纖程。英文名Coroutine協程看上去也是子程序,但執行過程中,在子程序內部可中斷,然后轉而執行別的子程序,在適當的時候再返回來接著執行。 最大的優勢就是協程極高...

    svtter 評論0 收藏0
  • 談談Python協程技術的演進

    摘要:事件循環是異步編程的底層基石。對事件集合進行輪詢,調用回調函數等一輪事件循環結束,循環往復。協程直接利用代碼的執行位置來表示狀態,而回調則是維護了一堆數據結構來處理狀態。時代的協程技術主要是,另一個比較小眾。 Coding Crush Python開發工程師 主要負責豈安科技業務風險情報系統redq。 引言 1.1. 存儲器山 存儲器山是 Randal Bryant 在《深入...

    zhiwei 評論0 收藏0

發表評論

0條評論

lolomaco

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<