python 協程

協程

  • 單線程下實現併發

  • 是一種輕量級的線程, 由程序本身控制調度的 線程是內核級的, 協程是程序級別的python

  • 優勢:程序員

    協程的切換開銷小,操做系統徹底感知不到, 單線程內就能夠實現併發效果,最大限度的 利用 CPU數組

  • 協程遇到 IO 就自動切換到其餘協程 檢測 IO yield greenlet 沒法實現併發

    用 gevent 模塊(select 機制) 實現 IO 的切換異步

  • 併發:

    僞並行,遇到IO就切換,單核下多個任務之間切換執行,給你的效果就是貌似你的幾個程序在同時執行.提升效率
    任務切換 + 保存狀態async

  • 並行:

    多核cpu,真正的同時執行函數

  • 串行:

    一個任務執行完在執行另一個任務spa

  • 首先,給出「進程、線程和協程」的特色:

    進程:擁有本身獨立的堆和棧,既不共享堆,也不共享棧,進程由操做系統調度;
    線程:擁有本身獨立的棧和共享的堆,共享堆,不共享棧,標準線程由操做系統調度;
    協程:擁有本身獨立的棧和共享的堆,共享堆,不共享棧,協程由程序員在協程的代碼裏顯示調度。操作系統

    import time
    '''串行 用時'''
    def func1():
        time.sleep(1)
        c = 0 
        for i in range(1000):
            c = i ** i
        print("func1>>", c)
    
    def func2():
        time.sleep(2)
        c = 0
        for i in range(1000):
            c = i ** i
        print("func2>>", c)
    
    
    if __name__ == '__main__':
        s_t = time.time()
        func1()
        func2()
        print(time.time() - s_t)  # 3.0608773231506348

    基於yield併發執行

    '''基於yield併發執行,多任務之間來回切換,這就是個簡單的協程的體現,可是他可以節省I/O時間嗎?不能'''
    
    def func1():
        while 1:
            time.sleep(1)
            x = yield
            print("接收了%s 個任務" % x)
    
    def func2():
        f = func1()
        # 找到 第一個 yield
        next(f)
        for i in range(1, 11):
            f.send(i)
            print("發送了 %s 個任務" % i)
    
    s_t = time.time()
    # 基於yield保存狀態,實現兩個任務直接來回切換,即併發的效果
    # PS: 若是每一個任務中都加上打印,那麼明顯地看到兩個任務的打印是你一次我一次,即併發執行的.
    
    func2()  # 我在當前線程中只執行了這個函數,可是經過這個函數裏面的send切換了另一個任務
    print(time.time() - s_t)  # 11.003206253051758
    
    
    # 串行的執行 方式
    # func1()
    # func2()
    # print(time.time() - s_t)  # 11.00475525856018

greenlet 模塊

  • 真正的協程模塊就是使用 greenlet 完成的切換
import time
from greenlet import greenlet

def eat(name):
    print('%s eat 1' % name)  # 2
    time.sleep(3)
    g2.switch('taibai')  # 3
    print('%s eat 2' % name)  # 6
    g2.switch()  # 7


def play(name):
    print('%s play 1' % name)  # 4
    time.sleep(3)
    g1.switch()  # 5
    print('%s play 2' % name)  # 8

g1 = greenlet(eat)
g2 = greenlet(play)

g1.switch('taibai')  # 能夠在第一次switch時傳入參數,之後都不須要

gevent 使用

  • 做用: 任務切換 + 保存狀態 實現了 IO 自動切換
import gevent
from gevent import monkey
monkey.patch_all()   # 補丁 識別全部的 IO 阻塞   time.sleep()  就能夠用了
import time

def func1(name):
    print(name, 111111111)
    time.sleep(1)
    # gevent.sleep(1)
    print(name, 2222222222)
    return 123


def func2(name):
    print(name, 111)
    # gevent.sleep(1)  # time.sleep() 識別不到
    time.sleep(1)
    print(name, 222)


g1 = gevent.spawn(func1, "egen")  # 異步執行這個func1任務,後面egon就是給他傳的參數
print(g1.value)  # 取出返回值的 結果
g2 = gevent.spawn(func2, "two")

# 此 方法 不用 單獨 指定 join()
gevent.joinall([g1, g2])
print("aaaaaa")
################################################################

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 = [gevent.spawn(task, i) for i in range(10)]
    gevent.joinall(g_l)


if __name__ == '__main__':
    print('Synchronous:')  # 同步的 併發 執行
    synchronous()
    print('Asynchronous:')  # 異步的 並行 執行
    asynchronous()
# 上面程序的重要部分是將task函數封裝到Greenlet內部線程的gevent.spawn。 初始化的greenlet列表存放在數組threads中,此數組被傳給gevent.joinall 函數,後者阻塞當前流程,並執行全部給定的greenlet。執行流程只會在 全部greenlet執行完後纔會繼續向下走。
相關文章
相關標籤/搜索