boost::coroutine2(stackful協程庫)

協程可以很輕量的在子例程中進行切換,它由程序員進行子例程的調度(即切換)而不像線程那樣需要內核參與,同時也省去了內核線程切換的開銷,因爲一個協程切換保留的就是函數調用棧和當前指令的寄存器,而線程切換需要陷入內核態,改變線程對象狀態。

go把協程作爲基礎設施提供語言級的支持,cpp這種出了名的給程序員自由的語言肯定不會提供語言級的支持,而是通過準標準庫boost coroutine2庫(boost coroutine已經廢棄,建議使用boost coroutine2)爲cpp提供的協程支持。


這裏有個來自boost coroutine2的例子可以說明協程的好處。假設有兩個函數


(圖片來自boost_1_65_1/libs/coroutine2/doc/html/coroutine2/intro.html)

協程可以在兩個子例程之前輕鬆切換交錯輸出,不使用協程就就需要把兩個子例程拆分成更小的子例程,如果期間涉及依賴上下文的計算那麼拆分也不行,只能考慮setjump/longjump或者線程等解決方案,顯然這樣一來腦力複雜度,代碼複雜度也就上來了。


協程分爲對稱協程(symmetric)和非對稱協程(asymmetric),對稱協程需要顯式指定將控制權yeild給誰,非對稱協程可以隱式的轉移控制權給它的調用者,boost coroutine2實現的是非對稱協程。最簡單的協程可以這樣:

現在來實現上圖的效果:

#include <iostream>
#include <boost/coroutine2/all.hpp>

void foo(boost::coroutines2::coroutine<void>::push_type & sink){
    std::cout << "a ";
    sink();
    std::cout << "b ";
    sink();
    std::cout << "c ";
}


int main(){
    boost::coroutines2::coroutine<void>::pull_type source(foo);
    std::cout << "1 ";
    source();
    std::cout << "2 ";
    source();
    std::cout << "3 ";
    getchar();
    return 0;
}
如果把push pull反過來會得到相反的輸出:

void foo(boost::coroutines2::coroutine<void>::pull_type & sink1){
    std::cout << "a ";
    sink1();
    std::cout << "b ";
    sink1();
    std::cout << "c ";
}

int main(){
    boost::coroutines2::coroutine<void>::push_type source(foo);
    std::cout << "1 ";
    source();
    std::cout << "2 ";
    source();
    std::cout << "3 ";
    return 0;
}
模板參數爲void的協程是特例化的實現,它比之普通的泛型協程少了get()和迭代器實現。這裏我們使用帶返回值的協程,然後用get方法獲取它的返回值:

#include <iostream>
#include <boost/coroutine2/all.hpp>

void foo(boost::coroutines2::coroutine<std::string>::pull_type & sink)
{
    std::cout << "get " << sink.get() << "from main() by foo()\n";
    sink();
    std::cout << "get " << sink.get() << "from main() by foo()\n";
    sink();
}

int main()
{
    std::string str1("HELLO");
    std::string str2("WORLD");
    boost::coroutines2::coroutine<std::string>::push_type source(foo);
    std::cout << "pass " << str1 << " from main() to foo()\n";
    source(str1);
    std::cout << "pass " << str2 << " from main() to foo()\n";
    source(str2);
    return 0;
}

不難看出push可以傳入參數,pull可以接受參數。

現在再試試協程的迭代器,對了,協程的迭代器不支持後置++:

#include <iostream>
#include <boost/coroutine2/all.hpp>
#include <boost/coroutine2/detail/push_coroutine.hpp>
#include <boost/coroutine2/detail/pull_coroutine.hpp>

constexpr int N = 10;

// 方法一:中規中矩
void foo(boost::coroutines2::coroutine<int>::pull_type & sink){
    using coIter = boost::coroutines2::coroutine<int>::pull_type::iterator;
    for (coIter start = begin(sink); start != end(sink); ++start) {
        std::cout << "retrieve "<<*start << "\n";
    }
}
// 方法二:auto自動推導
void foo2(boost::coroutines2::coroutine<int>::pull_type & sink) {
    for (auto val : sink) {
        std::cout << "retrieve " << val << "\n";
    }
}
// 方法三:守舊
void foo3(boost::coroutines2::coroutine<int>::pull_type & sink) {
    for (int i = 0; i < N; i++) {
        std::cout << "retrieve " << sink.get() << "\n";
        sink();
    }
}

int main(){
    boost::coroutines2::coroutine<int>::push_type source(foo2);
    for (int i = 0; i < N; i++) {
        source(i);
    }
    return 0;
}


發佈了63 篇原創文章 · 獲贊 51 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章