日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

C++中的yield和fork

發(fā)布時間:2025/3/21 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++中的yield和fork 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

各位看官,您沒有看錯,C++是可以有yield和fork的,這個主題小麥很早以前就打算寫,只是一直沒有一個契機給我這個動力。前段日子,小麥幫朋友處理一個用單線程模擬多線程的活兒的時候,再次想到了這個事情,決定寫一下,也算是自己的一個回顧。本文其實也可以叫做boost::asio::coroutine原理解析。。。

當然,C++本身是沒有yield這個關鍵字的,這個關鍵字的意義可以參考C#對其的定義,就是跳出當前的執(zhí)行過程,下次調用這個過程的時候,直接從yield處開始執(zhí)行。例如

int foo() {yield return 1;yield return 2;yield return 3; }

連續(xù)調用foo函數(shù)3次,分別得到了1,2,3這三個數(shù),這就是yield的基本語義,而fork的意思是保存當前上下文,跳轉到目標函數(shù),在目標函數(shù)執(zhí)行完成之后,回到當前位置,例如

std::cout<<"line 1"<<std::endl; fork foo()

從上面的例子可以看出,yield和fork的作用就相當于用單線程模擬了多線程的語義。這個語義正是常說的coroutine,即協(xié)程。通常來說,協(xié)程的實現(xiàn)是保存當前上下文,然后在線程內(nèi)不斷切換上下文,這種情況也叫做stackfull coroutine.本文要描述的是不保存上下文的情況,即stackless coroutine。

怎么用

如果各位看過C#中關于 yield的實現(xiàn)的話,可能會有一些印象,C#把yield語句翻譯成了一個狀態(tài)機,利用狀態(tài)機記錄每次進入函數(shù)時應該從何處開始執(zhí)行。然而在C++中,失去了編譯器的支持,想實現(xiàn)這個語義還是頗為困難的。下面的實現(xiàn)是boost asio的作者給出的實現(xiàn),其逆天程度之高,以致于這個實現(xiàn)最開始并沒有出現(xiàn)在boost asio的官方文檔中,而是在boost asio的example中靜靜的躺著,直到最近的boost 1.54,這個技術才正式成為boost asio的一部分,

簡而言之,這個實現(xiàn)是用宏實現(xiàn)的,請各位忘記C++的程序跳轉,記住C#的yield,我們來看個例子

#include <boost/asio/yield.hpp> #include <boost/asio/coroutine.hpp> #include <iostream>boost::asio::coroutine c;void foo(int i) {reenter(c){yield std::cout<<"foo1 "<<i<<std::endl;fork foo(100);yield std::cout<<"foo2 "<< i+1<<std::endl;} } int main() {foo(1);foo(2);foo(3);return 0; }

這個程序的輸出是。。。

foo1 1 foo2 101 foo2 3

這個咋看之下有點難以理解,我們先忽略具體的細節(jié),解釋一下這是怎么回事。

首先,我們需要聲明一個corountine,然后將需要重復進入的代碼用reenter包括起來。第一次調用foo的時候,代碼執(zhí)行到第一個yield,此時,foo直接返回。

第二次調用的時候,程序直接執(zhí)行上一次yield之后的代碼,即fork,此時,程序調用foo,需要注意的是,被調用的fork不再執(zhí)行第一個yield,而是直接從當前語句開始執(zhí)行,于是得到輸出foo2 101,返回之后,程序調用fork之后的yield,得到輸出foo2 3

第三次調用的時候,由于不再有任何未執(zhí)行的yield,因此不再產(chǎn)生任何輸出。

很自然的,我們會想像C#中的yield一樣使用,類似這樣

int nums() {reenter(c){for(int i = 0; i < 10; i++){yield return i;}} }

很遺憾,這是不行的,這會有一個編譯錯誤,Switch case is in protected scope,這顯然讓人覺得不可思議,聯(lián)想到我們寫的程序本身就不太像正常的C++代碼,我們有必要詳細了解一下到底發(fā)生了什么。

內(nèi)部實現(xiàn)

首先,我們先從能看見的類開始,coroutine

class coroutine{public:/// Constructs a coroutine in its initial state.coroutine() : value_(0) {}/// Returns true if the coroutine is the child of a fork.bool is_child() const { return value_ < 0; }/// Returns true if the coroutine is the parent of a fork.bool is_parent() const { return !is_child(); }/// Returns true if the coroutine has reached its terminal state.bool is_complete() const { return value_ == -1; }private:friend class detail::coroutine_ref;int value_;};namespace detail {class coroutine_ref{public:coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}~coroutine_ref() { if (!modified_) value_ = -1; }operator int() const { return value_; }int& operator=(int v) { modified_ = true; return value_ = v; }private:void operator=(const coroutine_ref&);int& value_;bool modified_;};} // namespace detail

上面這兩個類,都很簡單,從類名就可以看出來這是啥意思,略過不提。重要的是如何用宏實現(xiàn)reenter、yield、fork。

reenter的定義是一個宏,名字有重定義,如下

#define BOOST_ASIO_CORO_REENTER(c) \switch (::boost::asio::detail::coroutine_ref _coro_value = c) \case -1: if (_coro_value) \{ \goto terminate_coroutine; \terminate_coroutine: \_coro_value = -1; \goto bail_out_of_coroutine; \bail_out_of_coroutine: \break; \} \else case 0:

回想一下,reenter的用法是

reenter(coroutine){ your_code...;}

因此your_code實際被放在case 0:中,回想一下_coro_value最初的初始化值是0,因此,第一次確實執(zhí)行了your_code

yield的定義同樣是個宏,定義如下

#define BOOST_ASIO_CORO_YIELD_IMPL(n) \for (_coro_value = (n);;) \if (_coro_value == 0) \{ \case (n): ; \break; \} \else \switch (_coro_value ? 0 : 1) \for (;;) \case -1: if (_coro_value) \goto terminate_coroutine; \else for (;;) \case 1: if (_coro_value) \goto bail_out_of_coroutine; \else case 0:

其中n的定義為__COUNTER___。

看到這個宏,估計看官和我最初反應差不多,深切的懷疑這個代碼的合法性,因為有這樣一種混亂的用法

switch(n) {for(...)case 1: if(...) }

這個事實上就是將case 1看作普通的類似goto使用的代碼標簽,也就是說可以通過switch到達,也可以通過for到達!

這里用戶的代碼仍然在case 0中出現(xiàn),當執(zhí)行這段代碼是,_coro_value的值首先被設置成一個程序標識用的整數(shù)n, 在最內(nèi)層的swithc-case中,_coro_value的值為n,因此,程序跳轉到case 0,首先執(zhí)行用戶的代碼,用戶代碼執(zhí)行完后,返回執(zhí)行for循環(huán),此時執(zhí)行對應的if語句,即goto bail_out_of_coroutine,這個定義在之前的reenter的宏中,此時將直接break,返回整個reenter的最外層。

再次調用函數(shù)時,由于_coro_value的值仍然為n,則直接調用case 0,直接執(zhí)行后續(xù)的語句。

fork的實現(xiàn)如下,這個小麥就不再具體解釋,各位看官請自行理解,比較簡單。

#define BOOST_ASIO_CORO_FORK_IMPL(n) \for (_coro_value = -(n);; _coro_value = (n)) \if (_coro_value == (n)) \{ \case -(n): ; \break; \} \else

至此,我們就理解了前面的程序為什么會出現(xiàn)一個編譯錯誤,因為大量的使用了switch-case,因此程序的層次很重要,不能在reenter中隨意修改 yield和fork的層次,否則會導致switch-case不匹配,從而出現(xiàn)編譯錯誤!!

總結

上述的用法并不是唯一的用法,各位還可以繼承boost::asio::coroutine,而不是定義變量,更多用法可以參考boost::asio的example。當然也可以和小麥探討哦~

《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的C++中的yield和fork的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。