C++20 新特性 協(xié)程 Coroutines(2)
想了解上一篇文章內(nèi)容的小伙伴可點(diǎn)擊 C++20 特性 協(xié)程 Coroutines (1)
談到什么是協(xié)程. 并且介紹了 co_yield
和 co_return
的作用. 這篇來(lái)介紹一下 co_await
.
1、co_await
一個(gè)形如:
co_await awaitable
的表達(dá)式就叫一個(gè) await-expression. co_await
表達(dá)式是用來(lái)暫停當(dāng)前協(xié)程的運(yùn)行, 轉(zhuǎn)而等待 awaitable
的結(jié)果. 然后 awaitable 進(jìn)行計(jì)算, 最終返回一個(gè) awaiter
結(jié)構(gòu)用來(lái)告訴 co_await
要做什么.
co_await
所在的函數(shù)塊本身就是協(xié)程, 所以這個(gè) co_await
也得配上一個(gè) promise
和一個(gè) coroutine_handle
. 就像上篇文章里面 generator
類之類的東西.
這個(gè) awaitable
可以是很多東西, 首先會(huì)檢查 promise 有沒(méi)有提供 await_transform
函數(shù), 如果有就會(huì)用上, 沒(méi)有就不管.
(只要提供了任何一個(gè) await_transform
, 那么每一個(gè) awaitable 都需要找到適合它的重載, 否則就會(huì)報(bào)錯(cuò). 庫(kù)的實(shí)現(xiàn)者可以通過(guò) await_transform 接口來(lái)限制哪些 awaitable 可以用在協(xié)程之中. 參見(jiàn)https://stackoverflow.com/q/65787797/14406396 )
之后的話, 會(huì)查找 operator co_await
這個(gè)函數(shù), 預(yù)期這個(gè) operator
返回一個(gè) awaiter
.已經(jīng)是一個(gè) awaiter
了.
2、awaiter 的三個(gè)接口用途
一個(gè) awaiter
需要實(shí)現(xiàn)三個(gè)接口 await_ready()
, await_suspend(std::coroutine_handle<P>) , await_resume() .
只要實(shí)現(xiàn)了這三個(gè)接口的東西就是 awaiter
.
await_ready()
告訴 co_await
自己好了沒(méi).
await_suspend(h)
可以選擇返回 void , bool , std::coroutine_handle<P> 之一. h 是本協(xié)程的 handle. P是本協(xié)程的 promise 類型 (或者是 void, 見(jiàn)第三篇中的解釋).
如果 await_ready()
返回 false , 這個(gè)協(xié)程就會(huì)暫停. 之后:
- 如果
await_suspend(h)
返回類型是 std::coroutine_handle<Z>, 那么就會(huì)恢復(fù)這個(gè) handle. 即運(yùn)行await_suspend(h).resume()
. 這意味著暫停本協(xié)程的時(shí)候, 可以恢復(fù)另一個(gè)協(xié)程. - 如果
await_suspend(h)
返回類型是 bool, 那么看 await_suspend(h) 的結(jié)果, 是 false 就恢復(fù)自己. - 如果
await_suspend(h)
返回類型是 void, 那么就直接執(zhí)行. 執(zhí)行完暫停本協(xié)程.
如果 await_ready()
返回 true 或者協(xié)程被恢復(fù)了, 那么就執(zhí)行 await_resume()
, 它得到的結(jié)果就是最終結(jié)果.
所以說(shuō), 這await_ready
, await_suspend
, await_resume
三個(gè)接口分別表示 "有沒(méi)有準(zhǔn)備好", "停不停", "好了該咋辦". 設(shè)計(jì)還是很自然的.
C++ 的協(xié)程是非對(duì)稱協(xié)程, 是有一個(gè)調(diào)用/被調(diào)用的關(guān)系的. 一個(gè)協(xié)程被某個(gè)東西喚醒了, 那么它下次暫停的時(shí)候, 就會(huì)把控制流還給那個(gè)喚醒它的東西. 所以 C++ 的協(xié)程完全可以看作是一個(gè)可重入的函數(shù).
3、協(xié)程用法的回顧
再來(lái)看上一篇文章中的偽代碼
{ promise-type promise(promise-constructor-arguments); try { co_await promise.initial_suspend(); // 創(chuàng)建之后 第一次暫停 function-body // 函數(shù)體 } catch ( ... ) { if (!initial-await-resume-called) throw; promise.unhandled_exception(); } final-suspend: co_await promise.final_suspend(); // 最后一次暫停 }
catch
塊里面出現(xiàn)的 !initial-await-resume-called
就是指 promise.initial_suspend()
返回的那個(gè) await_resume()
有沒(méi)有被執(zhí)行過(guò).
如果執(zhí)行了, 那么這個(gè) flag 就會(huì)立刻變成 true. 然后調(diào)用 promise.unhandled_exception() 來(lái)處理異常.
一個(gè)例子:
由于 co_await
對(duì)這三個(gè)東西的應(yīng)該做什么沒(méi)有做任何限制, 所以可以用來(lái)實(shí)現(xiàn)很多功能.
舉個(gè)例子 (來(lái)自標(biāo)準(zhǔn)庫(kù)), 比如我們想要設(shè)計(jì)一個(gè)協(xié)程, 能夠停下任意的正時(shí)長(zhǎng), 就可以這樣設(shè)計(jì):
template <class Rep, class Period> auto operator co_await(std::chrono::duration<Rep, Period> d) // operator co_await { struct awaiter { std::chrono::system_clock::duration duration; awaiter(std::chrono::system_clock::duration d) : duration(d) {} bool await_ready() const { return duration.count() <= 0; } int await_resume() { return 1; } void await_suspend(std::coroutine_handle<> h) { std::this_thread::sleep_for(duration); } }; return awaitervvxyksv9kd; }
這樣的話, 如果輸入一個(gè)正的時(shí)間, 就會(huì)調(diào)用 await_suspend()
進(jìn)行暫停了. 如果輸入的時(shí)間是負(fù)的, 那就通過(guò) await_ready()
返回 true 繞過(guò)了這個(gè)過(guò)程.
當(dāng)然, 調(diào)用它需要在一個(gè)協(xié)程中, 也就意味著需要一個(gè) promise
和 coroutine_handle
包裝類的配合. 像這樣
struct my_future { struct promise_type; using handle = std::coroutine_handle<promise_type>; struct promise_type { int current_value; auto initial_suspend() { return std::suspend_always{}; } auto final_suspend() { return std::suspend_always{}; } void unhandled_exception() { std::terminate(); } /* ... */ }; /* ... */ private: my_future(handle h) : coro(h) {} handle coro; }; my_future sleep_coro() { printf("Start sleeping\n"); int ans = co_await 1s; printf("End sleeping, with ans = %d\n", ans); }
當(dāng)然, 一個(gè)函數(shù)也可以放在 co_await
的右邊, 就像 co_await g();
只要返回的結(jié)構(gòu)里面有那三個(gè) await_* 接口就行. 甚至你可以直接 co_await std::suspend_always{};
下面是協(xié)程流控的細(xì)致分析.
int main() { auto h = sleep_coro(); // 這一步創(chuàng)建協(xié)程, 在 co_await initial_suspend 處, 執(zhí)行完 await_ready, await_suspend. 返回 main // 注意 initial_suspend 返回的是 std::suspend_always{} // 所以是一定暫停, 并且 resume 的時(shí)候什么都不做 h.resume(); // 這一步執(zhí)行上一個(gè) await_resume 以后(什么都不做), 執(zhí)行了 printf("Start sleeping\n"); // 然后收到 co_await 1s 返回的結(jié)構(gòu), 其中 await_suspend 里面需要暫停. // 然后執(zhí)行完 await_ready, await_suspend (在這個(gè)函數(shù)里暫停 1s), 返回 main h.resume(); // 這一步執(zhí)行完 await_resume 以后(初始化 ans = 1) // 執(zhí)行了 printf("End sleeping, with ans = %d\n", ans); // 然后在 co_await final_suspend 處執(zhí)行完 await_ready, await_suspend. 就返回 main }
示列代碼見(jiàn)這里
到這里大家可以重新會(huì)到(1)去看看:C++20 特性 協(xié)程 Coroutines(1)
到此這篇關(guān)于C++20 新特性 協(xié)程 Coroutines(2)的文章就介紹到這了,更多相關(guān)C++20 協(xié)程 Coroutines
內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言使用函數(shù)實(shí)現(xiàn)字符串部分復(fù)制問(wèn)題
這篇文章主要介紹了C語(yǔ)言使用函數(shù)實(shí)現(xiàn)字符串部分復(fù)制問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11C++ 流插入和流提取運(yùn)算符的重載的實(shí)現(xiàn)
這篇文章主要介紹了C++ 流插入和流提取運(yùn)算符的重載的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Visual?Studio?Code?配置C、C++?文件debug調(diào)試環(huán)境的詳細(xì)過(guò)程
這篇文章主要介紹了Visual?Studio?Code?配置C、C++?文件debug調(diào)試環(huán)境,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02關(guān)于VS2019 C++項(xiàng)目同時(shí)出現(xiàn)LNK2005 和LNK1169 error 的解決辦法
這篇文章主要介紹了關(guān)于VS2019 C++項(xiàng)目同時(shí)出現(xiàn)LNK2005 和LNK1169 error 的解決辦法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04