對協程的一點理解

什麼是協程

A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes.算法

也就是說,協程是一個函數,能夠被掛起和被恢復。編程

協程不是被操做系統內核所管理,而徹底是由程序所控制(也就是在用戶態執行)。這樣帶來的好處就是性能獲得了很大的提高,不會像線程那樣須要上下文切換來消耗資源,所以協程的開銷遠遠小於線程的開銷。多線程

子程序

子程序,就是函數,在全部語言中都是層級調用,好比A調用B,B在執行過程當中又調用了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。函數

協程看上去也是子程序,但執行過程當中,在子程序內部可中斷,而後轉而執行別的子程序,在適當的時候再返回來接着執行。oop

注意,在一個子程序中中斷,去執行其餘子程序,不是函數調用,有點相似CPU的中斷。好比子程序A、B:post

def A():
    print '1'
    print '2'
    print '3'

def B():
    print 'x'
    print 'y'
    print 'z'

假設由協程執行,在執行A的過程當中,能夠隨時中斷,去執行B,B也可能在執行過程當中中斷再去執行A,結果多是:性能

1
2
x
y
3
z

可是在A中是沒有調用B的,因此協程的調用比函數調用理解起來要難一些。spa

看起來A、B的執行有點像多線程,但協程的特色在因而一個線程執行,那操作系統

和多線程比,協程有何優點?線程

1. 切換開銷小,執行效率高

由於子程序切換不是線程切換,而是由程序自身控制

2. 不須要多線程的鎖機制

由於只有一個線程,也不存在同時寫變量衝突,在協程中控制共享資源不加鎖,只須要判斷狀態就行了

子程序與協程的差別:

協程能夠經過yield(取其「讓步」之義而非「出產」)來調用其它協程,接下來的每次協程被調用時,從協程上次yield返回的位置接着執行,經過yield方式轉移執行權的協程之間不是調用者與被調用者的關係,而是彼此對稱、平等的

1. 子程序能夠調用其餘子程序,調用者等待被調用者結束後繼續執行,故而子程序的生命期遵循後進先出,即最後一個被調用的子例程最早結束返回。協程的生命期徹底由對它們的使用須要來決定。(也就是想怎麼跳就怎麼跳,不須要遵循函數調用那套規則)

2. 子程序的起始處是唯一的入口點,而協程能夠有多個入口點,協程的起始處是第一個入口點,每一個yield返回出口點都是再次被調用執行時的入口點。

3. 子例程只在結束時一次性的返回所有結果值。協程能夠在yield時不調用其餘協程,而是每次返回一部分的結果值,這種協程常稱爲生成器迭代器

子例程是協程的特里,由於任何子例程均可以看做是不調用yield的協程

示例

這裏是一個簡單的例子證實協程的實用性。

傳統的生產者-消費者模型是一個線程寫消息,一個線程取消息,經過鎖機制控制隊列和等待,但一不當心就可能死鎖。

若是改用協程,生產者生產消息後,直接經過yield跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼續生產,效率極高:

var q := 新建隊列

coroutine 生產者
    loop
        while q 不滿載
            創建某些新產品
            向 q 增長這些產品 
        yield 給消費者

coroutine 消費者
    loop
        while q 不空載
            從 q 移除某些產品
            使用這些產品
        yield 給生產者

應用場景

根據今天查閱的資料來看,協程的應用場景主要在於 :I/O 密集型任務。

當程序在執行 I/O 時操做時,時間片的算法並不知道,就分配了時間片給他,此時CPU 是空閒的,所以能夠充分利用 CPU 的時間片來處理其餘任務。
- 在單線程中,一個函數調用,通常是從函數的第一行代碼開始執行,結束於 return 語句、異常或者函數執行(也能夠認爲是隱式地返回了 None )。(至關於被阻塞了,CPU就被浪費了)
- 有了協程,咱們在函數的執行過程當中,若是遇到了耗時的 I/O 操做,函數能夠臨時讓出控制權,讓 CPU 執行其餘函數,等 I/O 操做執行完畢之後再收回控制權。(至關於暫時跳過去)

參考連接

相關文章
相關標籤/搜索