嘉鱼网站建设哪家好,射阳网站设计,wordpress 百度蜘蛛,wordpress+搬瓦工迁移C20新特性解析#xff1a;深入探讨协程库的实现原理与应用 一、C20的协程库简介二、C20协程基础知识2.1、协程的基本概念和使用方法2.2、C20中的协程支持2.3、协程与传统线程的对比 三、C20协程库的实现原理四、C20协程库的应用实例总结 一、C20的协程库简介
C20引入了对协程… C20新特性解析深入探讨协程库的实现原理与应用 一、C20的协程库简介二、C20协程基础知识2.1、协程的基本概念和使用方法2.2、C20中的协程支持2.3、协程与传统线程的对比 三、C20协程库的实现原理四、C20协程库的应用实例总结 一、C20的协程库简介
C20引入了对协程的支持这是一项重要的编程语言特性可以简化异步编程的实现而且提高代码的可读性和可维护性。协程可以在执行过程中暂停和恢复能够更直观地表达异步操作的流程让编程更加简洁和高效。
C20的协程库提供了一组新的关键字、库函数和概念能轻松地实现异步操作、事件驱动的编程模型和无阻塞式IO等。这些特性对于网络编程、并发编程和响应式编程都有很大的帮助。在C20之前一般都是使用第三方库或者自己实现协程功能现在C20的协程库为协程的使用提供了官方标准的支持为C编程带来了全新的可能性。
协程在异步编程中的重要性 传统的异步编程方式如回调、Promise等会使代码结构复杂、难以理解和维护。而协程可以让异步代码看起来更像是同步代码通过暂停和恢复来表达异步操作的逻辑。 协程可以大大简化异步代码的写法避免回调地狱callback hell和层层嵌套的问题。 相较于传统的基于回调的异步编程方式协程可以更高效地利用系统资源减少上下文切换和线程调度的开销提高程序的性能。 协程中的暂停和恢复让状态管理非常便利更轻松地处理异步操作中的状态和上下文切换。 二、C20协程基础知识
2.1、协程的基本概念和使用方法
协程是一种轻量级线程它可以在不同的执行上下文中暂停和恢复。在协程中程序可以通过显式的暂停和恢复操作来控制执行流程能够更灵活地管理并发任务。
协程的基本概念 暂停和恢复协程可以在执行过程中暂停自己并在之后的某个时间点恢复执行。这种暂停和恢复是由程序员显式地控制的可以在任何地方发生。 轻量级线程与传统的操作系统线程相比协程更加轻量级可以在同一个线程中并发执行多个协程从而减少线程切换的开销。 异步编程协程通常用于异步编程可以在 I/O 操作和其他耗时的任务中进行暂停和恢复提高程序的并发性能。
在C20中协程使用co_await和co_yield等关键字来实现暂停和恢复操作。通过使用co_await可以将执行权交还给调用者同时将当前状态保存起来。而co_yield用于向调用者返回一个值同时也会暂停当前协程的执行。协程也需要一个可调用的函数作为入口点称为协程函数。
示例
#include iostream
#include coroutine
/*******************************************************************
* promise_type结构定义协程的状态和控制逻辑
* initial_suspend和final_suspend用于定义协程的初始化和结束时的暂停行为。
* get_return_object方法返回一个Generator对象
* return_void用于处理协程返回的结果
* yield_value用于返回一个值并暂停协程的执行。
********************************************************************/
struct Generator {struct promise_type {int current_value;std::exception_ptr exception; // 用来存储异常指针// 初始挂起不做任何事情auto initial_suspend() { return std::suspend_always{}; }// 最终挂起销毁coroutineauto final_suspend() noexcept { return std::suspend_always{}; }Generator get_return_object() {return Generator{std::coroutine_handlepromise_type::from_promise(*this)};}// 返回时不做任何事情void return_void() {}// 挂起并记下当前valueauto yield_value(int value) {current_value value;return std::suspend_always{};}// 存储异常void unhandled_exception() { exception std::current_exception(); }};std::coroutine_handlepromise_type coro;explicit Generator(std::coroutine_handlepromise_type h) : coro(h) {}~Generator() {// 解构时销毁coroutineif (coro)coro.destroy();}int getValue() {// 无异常时返回当前值否则重新抛出异常if (coro.promise().exception)std::rethrow_exception(coro.promise().exception);return coro.promise().current_value;}bool next() { if (!coro.done()) {coro.resume();return !coro.done();}return false;}
};Generator counter() {for (int i 0; i 5; i)co_yield i;
}int main() {Generator gen counter();while (gen.next()) {std::cout gen.getValue() std::endl;}return 0;
}编译时一定要加上-stdc20选项。
在counter函数中使用co_yield将值返回给调用者同时暂停协程的执行。在main函数通过Generator对象的next方法来依次取出协程返回的值并输出到控制台。
这就是一般情况下C中协程的基本概念和使用方法。
2.2、C20中的协程支持
C20中引入了对协程的原生支持可以直接利用协程来编写异步程序。协程支持是通过引入一组新的关键字和库来实现的包括co_await、co_yield、co_return等关键字以及相关的标准库函数和类型。
关键字/库描述co_await表示在异步操作完成前将控制权交给调用方co_yield在协程中产生一个值并暂停执行co_return表示协程执行结束并返回值std::coroutine_handle一个用于控制协程句柄的类std::suspend_always一个永远暂停的协程suspend点通常用于展示示例以进行协程暂停和恢复的操作std::suspend_never一个从不暂停的协程suspend点通常用于展示示例以进行协程的初始和最终操作
C20使用协程进行异步编程
引入coroutine头文件该头文件包括了与协程相关的标准库函数和类型在函数声明或定义中使用co_await关键字表示在异步操作完成之前将控制权交给调用方使用co_yield关键字来在协程中产生一个值并暂停执行在协程的返回值上使用co_return关键字表示协程执行结束并返回值。 #mermaid-svg-1n2W4b4jCOrl3K7D {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1n2W4b4jCOrl3K7D .error-icon{fill:#552222;}#mermaid-svg-1n2W4b4jCOrl3K7D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1n2W4b4jCOrl3K7D .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-1n2W4b4jCOrl3K7D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1n2W4b4jCOrl3K7D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1n2W4b4jCOrl3K7D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1n2W4b4jCOrl3K7D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1n2W4b4jCOrl3K7D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1n2W4b4jCOrl3K7D .marker.cross{stroke:#333333;}#mermaid-svg-1n2W4b4jCOrl3K7D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1n2W4b4jCOrl3K7D .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1n2W4b4jCOrl3K7D .cluster-label text{fill:#333;}#mermaid-svg-1n2W4b4jCOrl3K7D .cluster-label span{color:#333;}#mermaid-svg-1n2W4b4jCOrl3K7D .label text,#mermaid-svg-1n2W4b4jCOrl3K7D span{fill:#333;color:#333;}#mermaid-svg-1n2W4b4jCOrl3K7D .node rect,#mermaid-svg-1n2W4b4jCOrl3K7D .node circle,#mermaid-svg-1n2W4b4jCOrl3K7D .node ellipse,#mermaid-svg-1n2W4b4jCOrl3K7D .node polygon,#mermaid-svg-1n2W4b4jCOrl3K7D .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1n2W4b4jCOrl3K7D .node .label{text-align:center;}#mermaid-svg-1n2W4b4jCOrl3K7D .node.clickable{cursor:pointer;}#mermaid-svg-1n2W4b4jCOrl3K7D .arrowheadPath{fill:#333333;}#mermaid-svg-1n2W4b4jCOrl3K7D .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1n2W4b4jCOrl3K7D .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1n2W4b4jCOrl3K7D .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-1n2W4b4jCOrl3K7D .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-1n2W4b4jCOrl3K7D .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1n2W4b4jCOrl3K7D .cluster text{fill:#333;}#mermaid-svg-1n2W4b4jCOrl3K7D .cluster span{color:#333;}#mermaid-svg-1n2W4b4jCOrl3K7D div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1n2W4b4jCOrl3K7D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 执行到co_yield 执行到co_await 执行到co_return 开始 创建promise对象 创建协程对象 调用 resume() 方法 协程执行 执行到 co_yield 关键字 执行到 co_await 关键字 执行协程等待操作 等待操作完成 调用 resume() 方法继续执行 结束 示例
#include iostream
#include coroutine// Define a struct named Task
struct Task {// Define a nested struct named promise_type for Taskstruct promise_type {// Return a default-constructed Task objectTask get_return_object() {return {};}// Suspend the coroutine indefinitely during its initial executionstd::suspend_never initial_suspend() {return {};}// Suspend the coroutine indefinitely at its final executionstd::suspend_never final_suspend() noexcept{return {};}// Terminate the program for unhandled exceptionsvoid unhandled_exception() {std::terminate();}// No action for coroutines returning voidvoid return_void() {}};~Task() { std::cout Task destroyed std::endl;}// Suspend the coroutine associated with the provided coroutine handlevoid await_suspend(std::coroutine_handlepromise_type) {}
};// Create an asynchronous function named async
Task async() {std::cout Async start std::endl;// Suspend the coroutine until being resumed (in this case indefinitely)co_await std::suspend_always{};std::cout Async end std::endl;
}int main() {// Call the async function and store the resulting taskauto task async();std::cout Main std::endl;return 0;
}Task是一个协程类型通过co_await来暂停执行并在适当的时机恢复执行。async函数是一个协程函数其中的co_await表示在异步操作完成前暂停执行。
输出
Async start
Main
Task destroyed2.3、协程与传统线程的对比
调度方式
传统线程是由操作系统的调度器进行管理和调度的它们可以并行执行在不同的物理核心上。线程的调度和切换需要内核的介入会消耗一定的系统资源。协程是由程序员显式地控制的它们运行在单一线程内部并且协程之间的切换必须经过协程函数的显式调用。协程的切换不需要内核介入并且开销较小。
内存占用
传统线程需要分配一定的内核资源来进行管理包括线程堆栈以及线程控制块等。因此创建大量的线程可能会占用大量的内存。协程在不同协程之间的切换通常只需要保存少量的上下文信息所以占用的内存较少。这也是协程在高并发的场景下具有优势。
编程模型
传统线程编程通常需要使用同步原语例如互斥锁、条件变量来处理共享资源的并发访问这增加了编程的复杂度。协程可以使用异步方式编写使用协程可以更自然地进行事件驱动的编程避免了使用传统线程编程中的锁和条件变量。
并发粒度
传统线程通常用于在多核处理器上并行执行代码适合于CPU密集型的任务。协程通常用于处理IO密集型的任务如网络请求文件读写等。它可以更高效地处理大量的并发IO操作。
三、C20协程库的实现原理
协程运行流程图
在底层实现中编译器会将协程转换为状态机。每个协程包含了一组状态比如挂起、运行、完成等并且通过调度器进行相互切换。当协程暂停时其状态会被保存下来等待下一次被唤醒时恢复执行。这种状态机的设计是实现协程的核心。
编译器会生成一些额外的代码来管理协程的状态和上下文切换。协程需要进行频繁的挂起和恢复操作因此涉及到堆栈和上下文的保存与恢复。编译器和标准库需要在底层处理这些操作保证协程的状态能够正确地切换和保存。
协程调度器和协程对象的概念 协程对象代表一个具体的协程实例它包含了协程的状态、执行过程中的数据等信息。在C20中使用co_await、co_yield等关键字来定义协程函数并创建对应的协程对象。 协程调度器负责协程的调度和管理它决定了哪个协程处于运行状态哪个协程被挂起或恢复。
协程调度器和协程对象的关系 协程对象依赖于协程调度器进行调度和管理。当一个协程对象需要暂停或者恢复执行时它会通过协程调度器来进行相应的操作。 协程对象通过协程调度器来执行并且协程调度器会负责协程的挂起、恢复、调度等操作。
四、C20协程库的应用实例
在异步IO操作中使用协程简化异步操作的编写提高代码的可读性、可维护性和性能。
协程将异步IO的回调地狱转换为顺序执行的代码代码更易于理解和编写。在异步IO场景下协程的轻量级和低开销的优势非常明显可以降低上下文切换的开销提高异步IO操作的性能和响应速度。自定义的协程调度器可以与异步IO操作结合实现灵活的异步IO调度和管理。C20协程可以与现有的异步IO框架如Boost.Asio、libuv等结合利用协程改善异步IO编程。
示例使用C20协程库搭配Boost.Asio来进行异步网络通信。
#include iostream
#include boost/asio.hpp
#include boost/asio/use_awaitable.hpp
#include coroutine
#include boost/asio/awaitable.hppnamespace net boost::asio;
using tcp boost::asio::ip::tcp;class AsyncTCPClient {
public:AsyncTCPClient(net::io_context io_context): resolver_(io_context), socket_(io_context) {}// Asynchronous connection to the servernet::awaitablevoid connect(const std::string host, unsigned short port) {auto results co_await resolver_.async_resolve(host, std::to_string(port), net::use_awaitable);co_await net::async_connect(socket_, results, net::use_awaitable);}// Asynchronous write operationnet::awaitablevoid write(const std::string data) {co_await net::async_write(socket_, net::buffer(data), net::use_awaitable);}// Asynchronous read operationnet::awaitablestd::string read(int max_length) {std::string data;data.resize(max_length);size_t read_length co_await socket_.async_read_some(net::buffer(data), net::use_awaitable);co_return data.substr(0, read_length);}private:tcp::resolver resolver_;tcp::socket socket_;
};int main() {net::io_context io_context;AsyncTCPClient client(io_context);co_spawn(io_context, [client]() - net::awaitablevoid {try {co_await client.connect(www.baidu.com, 80);co_await client.write(GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n);std::string response co_await client.read(1024);std::cout Received: response std::endl;} catch (std::exception e) {std::cerr Exception: e.what() std::endl;}}, net::detached);io_context.run();return 0;
}要先安装boost库。
sudo apt-get install libboost-all-dev编译
g -stdc20 -lboost_system -lboost_coroutine -lboost_context -pthread testp.cc -o testp输出
Received: HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Content-Length: 9508
Content-Type: text/html
Date: Sun, 07 Jan 2024 06:15:56 GMT
P3p: CP OTI DSP COR IVA OUR IND COM
P3p: CP OTI DSP COR IVA OUR IND COM
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BAIDUID84A68B6C381FF605A97D9FCB3889B2E7:FG1; expiresThu, 31-Dec-37 23:55:55 GMT; max-age2147483647; path/; domain.baidu.com
Set-Cookie: BIDUPSID84A68B6C381FF605A97D9FCB3889B2E7; expiresThu, 31-Dec-37 23:55:55 GMT; max-age2147483647; path/; domain.baidu.com
Set-Cookie: PSTM1704608156; expiresThu, 31-Dec-37 23:55:55 GMT; max-age2147483647; path/; domain.baidu.com
Set-Cookie: BAIDUID84A68B6C381FF6058825F825739832C9:FG1; max-age31536000; expiresMon, 06-Jan-25 06:15:56 GMT; domain.baidu.com; path/; version1; commentbd
Traceid: 1704608156352853428210666740912840254115
Vary: Accept-Encoding
X-Ua-Compatible: IEEdge,chrome1
Connection: close!DOCTYPE htmlhtmlheadmeta http-equivContent-Type contentte总结
未来的C标准可能会继续完善和扩展协程库包括新增更多的协程相关工具、函数和语法糖来满足更广泛的并发编程需求。未来会有更好的编译器支持包括优化协程性能、提供更丰富的调试信息等。
C20协程库会推动标准化并发编程模式包括并发任务调度、异步操作、协同执行等未来会进一步整合协程库与其他标准库比如网络库、文件系统库等提供更完整的异步操作和并发编程支持。
除了简单的协程机制未来也会有更丰富的并发编程模型比如actor模型、数据流编程模型等。也会进一步优化协程的性能包括降低协程的开销、提高协程的并发性能等以提供更高效的并发编程支持。
参考文献
【Google “战败”后C20 用微软的提案进入协程时代】【基于C20协程库封装的开源框架】【协程 (C20)官方文档】