網(wǎng)站首頁 編程語言 正文
一、關(guān)于協(xié)程
從 1.54.0 版本開始,Boost.Asio 支持協(xié)程。雖然您可以直接使用 Boost.Coroutine,但 Boost.Asio 中對協(xié)程的顯式支持使得使用它們變得更加容易。
協(xié)程讓您創(chuàng)建一個反映實(shí)際程序邏輯的結(jié)構(gòu)。異步操作不會拆分函數(shù),因?yàn)闆]有處理程序來定義異步操作完成時應(yīng)該發(fā)生什么。程序可以使用順序結(jié)構(gòu),而不是讓處理程序相互調(diào)用。
二、協(xié)程的好處
考慮多任務(wù)協(xié)作的場景. 如果是線程的并發(fā), 那么大家需要搶 CPU
用, 還需要條件變量/信號量或者上鎖等技術(shù), 來確保正確的線程正在工作.
如果在協(xié)程中, 大家就可以主動暫停自己, 多個任務(wù)互相協(xié)作. 這樣可能就比大家一起搶 CPU
更高效一點(diǎn), 因?yàn)槟隳軌蚩刂颇膫€協(xié)程用上 CPU
.
一個例子:
生產(chǎn)者/消費(fèi)者模型: 生產(chǎn)者生產(chǎn)完畢后, 暫停自己, 把控制流還給消費(fèi)者. 消費(fèi)者消費(fèi)完畢后, resume
生產(chǎn)者, 生產(chǎn)者繼續(xù)生產(chǎn). 這樣循環(huán)往復(fù).
異步調(diào)用: 比如你要請求網(wǎng)絡(luò)上的一個資源.
- 發(fā)請求給協(xié)程
- 協(xié)程收到請求以后, 發(fā)出請求. 協(xié)程暫停自己, 把控制權(quán)還回去.
- 你繼續(xù)做些別的事情. 比如發(fā)出下一個請求. 或者做一些計(jì)算.
- 恢復(fù)這個協(xié)程, 拿到資源 (可能還要再等一等)
理想狀態(tài)下, 4 可以直接用上資源, 這樣就完全不浪費(fèi)時間.
如果是同步的話:
- 發(fā)請求給函數(shù).
- 函數(shù)收到請求以后, 等資源.
- 等了很久, 資源到了, 把控制權(quán)還回去.
明顯需要多等待一會兒. 如果需要發(fā)送上百個請求, 那顯然是第一種異步調(diào)用快一點(diǎn). (等待的過程中可以發(fā)送新的請求)
如果沒有協(xié)程的話, 解決方案之一是使用多線程. 像這樣:
- 發(fā)請求給函數(shù).
- 函數(shù)在另外的線程等, 不阻塞你的線程.
- 你繼續(xù)做些別的事情. 比如發(fā)出下一個請求. 或者做一些計(jì)算.
- 等到終于等到了, 他再想一些辦法通知你.
然后通知的辦法就有 promise
和回調(diào)這些辦法.
三、協(xié)程得用法
我們照著 C++20 標(biāo)準(zhǔn)來看看怎么用協(xié)程. 用 g++, 版本 10.2 進(jìn)行測試.
目前 C++20 標(biāo)準(zhǔn)只加入了協(xié)程的基本功能, 還沒有直接能上手用的類. GCC 說會盡量與 clang
和 MSVC
保持協(xié)程的 ABI 兼容, 同時和 libc++ 等保持庫的兼容. 所以本文可能也適用于它們.
協(xié)程和主程序之間通過 promise
進(jìn)行通信. promise
可以理解成一個管道, 協(xié)程和其調(diào)用方都能看得到.
以前的 std::async
和 std::future
也是基于一種特殊的 promise
進(jìn)行通信的, 就是 std::promise
. 如果要使用協(xié)程, 則需要自己實(shí)現(xiàn)一個全新的 promise
類, 原理上是類似的.
四、與線程的區(qū)別
線程處于進(jìn)程之中,協(xié)程處于線程之中,線程有系統(tǒng)內(nèi)核調(diào)度,而協(xié)程有程序員自己調(diào)度。一個線程可以有多個協(xié)程,而且只要內(nèi)存足夠,一個線程中可以有任意多個協(xié)程;但某一時刻只能有一個協(xié)程在運(yùn)行,多個協(xié)程分享該線程分配到的計(jì)算機(jī)資源。協(xié)程是追求極限性能和優(yōu)美的代碼結(jié)構(gòu)的產(chǎn)物。
使用過程中需要包含#include <boost/coroutine2/all.hpp>,鏈接動態(tài)庫:-lboost_coroutine -lboost_context。關(guān)于使用boost庫錯
協(xié)程有如下特點(diǎn):
- 同其他數(shù)據(jù)類型一樣,協(xié)程也是第一類(first-class)對象,可以被當(dāng)參數(shù)傳遞等操作;
- 運(yùn)行特點(diǎn)是掛起運(yùn)行,離開協(xié)程,過后再進(jìn)入,恢復(fù)運(yùn)行;
- 具有對稱和非對稱的轉(zhuǎn)移控制機(jī)制;
- 掛起前和恢復(fù)后本地變量的值是一致的;
- 有stackless和stackful兩種類型
五、協(xié)程示例
示例 32.7。使用 Boost.Asio 的協(xié)程
#include <boost/asio/io_service.hpp> #include <boost/asio/spawn.hpp> #include <boost/asio/write.hpp> #include <boost/asio/buffer.hpp> #include <boost/asio/ip/tcp.hpp> #include <list> #include <string> #include <ctime> using namespace boost::asio; using namespace boost::asio::ip; io_service ioservice; tcp::endpoint tcp_endpoint{tcp::v4(), 2014}; tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint}; std::list<tcp::socket> tcp_sockets; void do_write(tcp::socket &tcp_socket, yield_context yield) { std::time_t now = std::time(nullptr); std::string data = std::ctime(&now); async_write(tcp_socket, buffer(data), yield); tcp_socket.shutdown(tcp::socket::shutdown_send); } void do_accept(yield_context yield) { for (int i = 0; i < 2; ++i) { tcp_sockets.emplace_back(ioservice); tcp_acceptor.async_accept(tcp_sockets.back(), yield); spawn(ioservice, [](yield_context yield) { do_write(tcp_sockets.back(), yield); }); } } int main() { tcp_acceptor.listen(); spawn(ioservice, do_accept); ioservice.run(); }
調(diào)用 Boost.Asio 使用協(xié)程的函數(shù)是 boost::asio::spawn()。傳遞的第一個參數(shù)必須是 I/O 服務(wù)對象。第二個參數(shù)是將成為協(xié)程的函數(shù)。此函數(shù)必須接受 boost::asio::yield_context 類型的對象作為其唯一參數(shù)。它必須沒有返回值。示例 32.7 使用 do_accept() 和 do_write() 作為協(xié)程。如果函數(shù)簽名不同,例如 do_write() 的情況,您必須使用類似 std::bind 的適配器或 lambda 函數(shù)。
您可以將 boost::asio::yield_context 類型的對象而不是處理程序傳遞給異步函數(shù)。 do_accept() 將參數(shù) yield 傳遞給 async_accept()。在 do_write() 中,yield 被傳遞給 async_write()。這些函數(shù)調(diào)用仍會啟動異步操作,但在操作完成時不會調(diào)用任何處理程序。而是恢復(fù)啟動異步操作的上下文。當(dāng)這些異步操作完成時,程序會從中斷的地方繼續(xù)。
do_accept() 包含一個 for 循環(huán)。每次調(diào)用該函數(shù)時,都會將一個新套接字傳遞給 async_accept()。一旦客戶端建立連接,do_write() 將作為協(xié)程調(diào)用,并帶有 boost::asio::spawn() 以將當(dāng)前時間發(fā)送給客戶端。
for 循環(huán)可以很容易地看出程序在退出之前可以為兩個客戶端提供服務(wù)。由于該示例基于協(xié)程,因此可以在 for 循環(huán)中實(shí)現(xiàn)異步操作的重復(fù)執(zhí)行。這提高了程序的可讀性,因?yàn)槟槐馗檶μ幚沓绦虻臐撛谡{(diào)用來找出最后一個異步操作何時完成。如果時間服務(wù)器需要支持兩個以上的客戶端,則只需調(diào)整 for 循環(huán)。
原文鏈接:https://blog.csdn.net/gongdiwudu/article/details/127602727
相關(guān)推薦
- 2022-01-31 git統(tǒng)計(jì)當(dāng)前項(xiàng)目代碼行數(shù)
- 2022-07-06 C語言中#pragma?once的作用_C 語言
- 2022-06-09 Python列表的索引與切片_python
- 2022-10-29 C++ 模板的顯式具體化
- 2022-12-07 聊聊C語言中sizeof運(yùn)算符的一個陷阱_C 語言
- 2024-01-28 springboot登錄認(rèn)證JWT令牌
- 2022-06-25 關(guān)于Ubuntu?Server?18.04?LTS?安裝Tomcat并配置systemctl管理To
- 2022-10-30 Android常用定時器的實(shí)現(xiàn)方式_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支