C語言標準庫之setjmp

協程的介紹

協程(coroutine),意思就是「協做的例程」(co-operative routines),最先由Melvin Conway在1963年提出並實現。跟主流程序語言中的線程不同,線程屬於侵入式組件,線程實現的系統稱之爲搶佔式多任務系統,而協程實現的多任務系統成爲協做式多任務系統。線程因爲缺少yield語義,因此運行過程當中不可避免須要調度,休眠掛起,上下文切換等系統開銷,還須要當心使用同步機制保證多線程正常運行。而協程的運行指令系列是固定的,不須要同步機制,協程之間切換也只涉及到控制權的交換,相比較線程來講是很是輕便的。不過同一時刻能夠有多個線程運行,但卻只能有一個協程運行。
實際上協程的概念比線程還要早,按照 Knuth 的說法「子例程是協程的特例」,一個子例程就是一次子函數調用,那麼實際上協程就是類函數同樣的程序組件,你能夠在一個線程裏面輕鬆建立數十萬個協程,就像數十萬次函數調用同樣。只不過子例程只有一個調用入口起始點,返回以後就結束了,而協程入口既能夠是起始點,又能夠從上一個返回點繼續執行,也就是說協程之間能夠經過 yield 方式轉移執行權,對稱(symmetric)、平級地調用對方,而不是像例程那樣上下級調用關係。固然 Knuth 的「特例」指的是協程也能夠模擬例程那樣實現上下級調用關係,這就叫非對稱協程(asymmetric coroutines)。多線程

setjmp.h

setjmp/longjmp  實際上是C語言標準庫中的內容,它被定義在<setjmp.h>頭文件中,我認識的至關部分的人包括寫過不少年C/C++的都表示沒聽過,而且他們在瞭解了一些setjmp的特性和功能以後還不覺得然,說我又不會用到它;然而大家想過爲何標準庫中會去實現一個相對這麼怪異特性的語法支持?緣由很簡單,就是爲了實現協程(coroutine),若是你一開始就給本身定位成協程的使用者,不關心它具體怎麼實現的,甚至給本身定位成從不用協程,後面的內容你放心能夠直接略過。
咱們首先來看 setjmp/longjmp 這兩個函數的定義。函數

int setjmp( jmp_buf _Buf );
void longjmp( jmp_buf _Buf, int _Value);

使用注意事項:
一、setjmp與longjmp結合使用時,它們必須有嚴格的前後執行順序,也即先調用setjmp函數,以後再調用longjmp函數,以恢復到先前被保存的「程序執行點」。不然,若是在setjmp調用以前,執行longjmp函數,將致使程序的執行流變的不可預測,很容易致使程序崩潰而退出

二、longjmp必須在setjmp調用以後,並且longjmp必須在setjmp的做用域以內。具體來講,在一個函數中使用setjmp來初始化一個全局標號,而後只要該函數不曾返回,那麼在其它任何地方均可以經過longjmp調用來跳轉到 setjmp的下一條語句執行。實際上setjmp函數將發生調用處的局部環境保存在了一個jmp_buf的結構當中,只要主調函數中對應的內存不曾釋放 (函數返回時局部內存就失效了),那麼在調用longjmp的時候就能夠根據已保存的jmp_buf參數恢復到setjmp的地方執行。
說白一點就是:在使用 setjmp 時,最多見的一個錯誤用法就是對它作封裝,不該該封裝在一個函數中。好比:spa

int try(breakpoint bp)
{
    return setjmp(bp->jb);
}

void throw(breakpoint bp)
{
    longjmp(bp->jb,1);
}

這樣寫並不會引發編譯錯誤,可是極易容易發生運行時錯誤,由於setjmp的棧是在try函數中,而下一次調用longjmp的時候try函數可能已經不在棧中被清除了。
來個簡單的例子:線程

#include <stdio.h>
#include <setjmp.h>

jmp_buf buf;
void second()
{
    printf("second\n");
    longjmp(buf, 1);
}
void first()
{
    second();
    printf("first\n");
}
int coro_main()
{
    if ( !(setjmp(buf)) )
    {
        first();
    }
    else
    {
        printf("main\n");
    }
    return 0;
}

輸出結果:code

second
main

除此以外還有廣爲使用的C語言協程非標準庫有 ucontext,據我所知ucontext應用更普遍一些,網上絕大多數 C 協程庫也是基於 ucontext 組件實現的。有空下次再去研究研究它。。。協程

相關文章
相關標籤/搜索