您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何理解C++20新特性協程Coroutines”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何理解C++20新特性協程Coroutines”吧!
1、co_await
2、awaiter 的三個接口用途
3、協程用法的回顧
想了解上一篇文章內容的小伙伴可點擊 C++20 特性 協程 Coroutines (1)
談到什么是協程. 并且介紹了 co_yield
和 co_return
的作用. 這篇來介紹一下 co_await
.
一個形如:
co_await awaitable
的表達式就叫一個 await-expression. co_await
表達式是用來暫停當前協程的運行, 轉而等待 awaitable
的結果. 然后 awaitable 進行計算, 最終返回一個 awaiter
結構用來告訴 co_await
要做什么.
co_await
所在的函數塊本身就是協程, 所以這個 co_await
也得配上一個 promise
和一個 coroutine_handle
. 就像上篇文章里面 generator
類之類的東西.
這個 awaitable
可以是很多東西, 首先會檢查 promise 有沒有提供 await_transform
函數, 如果有就會用上, 沒有就不管.
(只要提供了任何一個 await_transform
, 那么每一個 awaitable 都需要找到適合它的重載, 否則就會報錯. 庫的實現者可以通過 await_transform 接口來限制哪些 awaitable 可以用在協程之中. 參見https://stackoverflow.com/q/65787797/14406396 )
之后的話, 會查找 operator co_await
這個函數, 預期這個 operator
返回一個 awaiter
.已經是一個 awaiter
了.
一個 awaiter
需要實現三個接口 await_ready()
, await_suspend(std::coroutine_handle<P>) , await_resume() .
只要實現了這三個接口的東西就是 awaiter
.
await_ready()
告訴 co_await
自己好了沒.
await_suspend(h)
可以選擇返回 void , bool , std::coroutine_handle<P> 之一. h 是本協程的 handle. P是本協程的 promise 類型 (或者是 void, 見第三篇中的解釋).
如果 await_ready()
返回 false , 這個協程就會暫停. 之后:
如果 await_suspend(h)
返回類型是 std::coroutine_handle<Z>, 那么就會恢復這個 handle. 即運行 await_suspend(h).resume()
. 這意味著暫停本協程的時候, 可以恢復另一個協程.
如果 await_suspend(h)
返回類型是 bool, 那么看 await_suspend(h) 的結果, 是 false 就恢復自己.
如果 await_suspend(h)
返回類型是 void, 那么就直接執行. 執行完暫停本協程.
如果 await_ready()
返回 true 或者協程被恢復了, 那么就執行 await_resume()
, 它得到的結果就是最終結果.
所以說, 這await_ready
, await_suspend
, await_resume
三個接口分別表示 "有沒有準備好", "停不停", "好了該咋辦". 設計還是很自然的.
C++ 的協程是非對稱協程, 是有一個調用/被調用的關系的. 一個協程被某個東西喚醒了, 那么它下次暫停的時候, 就會把控制流還給那個喚醒它的東西. 所以 C++ 的協程完全可以看作是一個可重入的函數.
再來看上一篇文章中的偽代碼
{ promise-type promise(promise-constructor-arguments); try { co_await promise.initial_suspend(); // 創建之后 第一次暫停 function-body // 函數體 } catch ( ... ) { if (!initial-await-resume-called) throw; promise.unhandled_exception(); } final-suspend: co_await promise.final_suspend(); // 最后一次暫停 }
catch
塊里面出現的 !initial-await-resume-called
就是指 promise.initial_suspend()
返回的那個 await_resume()
有沒有被執行過.
如果執行了, 那么這個 flag 就會立刻變成 true. 然后調用 promise.unhandled_exception() 來處理異常.
一個例子:
由于 co_await
對這三個東西的應該做什么沒有做任何限制, 所以可以用來實現很多功能.
舉個例子 (來自標準庫), 比如我們想要設計一個協程, 能夠停下任意的正時長, 就可以這樣設計:
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 awaiteraegqsqibtmh; }
這樣的話, 如果輸入一個正的時間, 就會調用 await_suspend()
進行暫停了. 如果輸入的時間是負的, 那就通過 await_ready()
返回 true 繞過了這個過程.
當然, 調用它需要在一個協程中, 也就意味著需要一個 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); }
當然, 一個函數也可以放在 co_await
的右邊, 就像 co_await g();
只要返回的結構里面有那三個 await_* 接口就行. 甚至你可以直接 co_await std::suspend_always{};
下面是協程流控的細致分析.
int main() { auto h = sleep_coro(); // 這一步創建協程, 在 co_await initial_suspend 處, 執行完 await_ready, await_suspend. 返回 main // 注意 initial_suspend 返回的是 std::suspend_always{} // 所以是一定暫停, 并且 resume 的時候什么都不做 h.resume(); // 這一步執行上一個 await_resume 以后(什么都不做), 執行了 printf("Start sleeping\n"); // 然后收到 co_await 1s 返回的結構, 其中 await_suspend 里面需要暫停. // 然后執行完 await_ready, await_suspend (在這個函數里暫停 1s), 返回 main h.resume(); // 這一步執行完 await_resume 以后(初始化 ans = 1) // 執行了 printf("End sleeping, with ans = %d\n", ans); // 然后在 co_await final_suspend 處執行完 await_ready, await_suspend. 就返回 main }
感謝各位的閱讀,以上就是“如何理解C++20新特性協程Coroutines”的內容了,經過本文的學習后,相信大家對如何理解C++20新特性協程Coroutines這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。