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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C 20 协程初探

發布時間:2023/12/2 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C 20 协程初探 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【導讀】:C 20 終于引入了協程特性,給庫作者提供了一個實現協程的機制,讓用戶方便使用協程來編寫異步邏輯,降低了異步并發編程的難度。結合我最近協程的學習,在這里記錄一下相關內容。

以下是正文


使用場景

協程和普通函數相比,多了個中途隨時?掛起?,隨后?恢復?的過程,當用戶調用一個阻塞請求接口,從而讓出控制權,當響應時,恢復之前的控制流,從而大大提高線程復用率,這也注意了協程只是并發的,并不是真正意義上的并行,在 IO 密集型場景下,協程能夠很好的提高資源利用率,用少數的線程達到并發成百上萬個協程的效果。

而相對傳統的線程池 回調模式,每發起一個請求,為了避免阻塞當前線程,需要掛一個回調函數處理后續過程,而回調函數又可能產生競爭,導致得加鎖處理。而協程卻能夠以同步方式寫實現異步,后續過程直接掛起,當響應的時候恢復執行。

我參與的項目中,對象隨時都可能起個線程干活,或者常駐于對象生命周期里,統計下來整個項目居然開了幾百個線程,由于多線程編程難免導致競爭,從而需要鎖這種很低級的機制做同步,而一旦引入了鎖,就不可避免的擴散開來,大家看到這里加把鎖,那我也加把鎖,統計下來代碼里面居然也有幾百把鎖。真是維護的噩夢。

由于協程能夠隨時掛起,后續恢復,這就能實現一些延遲計算的特性,例如生成器。

扯遠了,本文主題是關于 C 20 的協程,在 C 20 還沒穩定之前,先來學習一下相關知識,讀完本文后你應該能利用這個機制實現一些想要的協程了。

概念模型

C 20 的協程設計為無棧協程,相對于有棧協程,省掉了上下文切換開銷[1],只能手動切換,效率更高,也不用管理復雜的寄存器狀態,移植性更好,但這同時也導致了不能被非協程函數嵌套調用。

同時引入了 3 個關鍵字:

1. co_yield: 掛起并返回值

2. co_await: 掛起

3. co_return: 結束協程

當一個函數出現了上面的關鍵字,則該函數是個協程。


Promise

當 caller 調用一個 callee 協程的時候,協程自身的狀態信息?[2](形參,局部變量,自帶數據,各個階段點執行點)會被保存在堆上的 Promise 對象中,這也是編譯器會在協程里面插入 Promise 相關代碼,以及一些執行點。由于 Promise 的大小可以在編譯期計算出來,從而避免了內存浪費。而 Promise 對象所有權可由coroutine_handle 句柄持有。

Future

而 Future 對象主要是與 Promise 對象交互的橋梁,既 caller 與 callee 之間的通信:

1. callee 掛起時,將值返回給 caller: yield 語義

2. callee 執行結束時,將值返回給 caller: return 語義

3. callee 恢復時,caller 將值帶給 callee

需要注意的是,這些概念和標準庫的 std::promise/std::future 不是同一個東西,后者用于做同步用,std::future會阻塞等待直到 std::promise 提供值,可以看做是條件變量的封裝,同樣地,和其他語言的 Promise/Future 概念也不一樣。

Awaitable

如果一個對象是 Awaitable 對象,那么可以用 co_await 操作符去觸發該對象的動作 ready/suspend/resume,從而轉移、恢復控制權,co_await 細節留到后面在介紹。

具體機制

了解了概念模型后,我們可以進一步探討背后的機制了。

Promise/Future 對象

當一個協程被調用時,會創建 Promise 對象,然后編譯器會在各個階段插入一些代碼[3]:

{??co_await?promise.initial_suspend();??try??{??????}??catch?(...)??{????promise.unhandled_exception();??}FinalSuspend:??co_await?promise.final_suspend();}

可以看到一個協程函數,分為如下幾個步驟:

1. 從堆上 (operator new) 創建 Promise 對象,保存協程的狀態信息

2. initial_suspend 階段,用于在執行協程主體??代碼前做些事情

3. 階段,執行協程的主體代碼

4. unhandled_exception 階段,若拋異常,處理異常

5. final_suspend階段,協程結束收尾動作,在這階段的 coroutine_handle::done 方法為 true,caller 可以通過這個方法判斷協程是否結束,從而不再調用 resume 恢復協程。

而協程返回類型則是一個 Future 對象,這一步編譯器通過 Promise::get_return_object()?來創建 Future 對象。而 Future 對象一般持有 Promise 的句柄:coroutine_handle,這樣 caller 可以通過 Future 與 Promise 交互,從而恢復協程。

而 Promise 對象釋放的時間點有兩個,避免重復執行,否則會 double free:

1. final_suspend 階段 resume 后

2. 調用?coroutine_handle::destroy()?方法

比較好的做法是在 final_suspend 階段掛起,這時候就不可 resume 了,在 caller 通過調用 Future 持有的句柄 destroy()?方法釋放 Promise 對象。綜上,一個 Promise 對象需要實現如下方法:

1. initial_suspend: 返回一個 Awaitable 對象

總結

以上是生活随笔為你收集整理的C 20 协程初探的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。