当前位置: 首页 > news >正文

温岭 网站建设个人网站设计内容

温岭 网站建设,个人网站设计内容,室内设计公司排名前100,青海省住房和城乡建设厅 网站一、c20的协程概念 在c20标准后#xff0c;在一些函数中看到co_await、co_yield、co_return这些关键词#xff0c;这是c20为协程实现设计的运算符。 协程是能暂停执行以在之后恢复的函数。原来我们调用一个功能函数时#xff0c;只要调用了以后#xff0c;就要完整执行完该…一、c20的协程概念 在c20标准后在一些函数中看到co_await、co_yield、co_return这些关键词这是c20为协程实现设计的运算符。 协程是能暂停执行以在之后恢复的函数。原来我们调用一个功能函数时只要调用了以后就要完整执行完该功能函数所有步骤语句才能回来执行自身的步骤对于一些功能函数其由很长的执行周期该执行周期中调用者就不能处理自身一些事务。在协程出现以前我们就需要回调、阻塞等手段综合设计实现。 协程就是为了解决类似这种问题的。调用者调用协程函数后执行到中途可以通过co_yield暂停挂起返回自身执行事务然后在通过resume唤醒恢复协程协程函数会从挂起标识处继续往下执行。整个协程函数执行周期内可以多次返回调用自身。在协程函数结束后通过co_return还可以返回协程结果或协程内部对象。 协程是无栈的它们通过返回到调用方暂停执行并且从栈分离存储恢复执行需要的数据。这样就可以编写异步执行的顺序代码例如不使用显式的回调来处理非阻塞 I/O还支持对惰性计算的无限序列上的算法及其他用途。  例如用关键词 co_yield 暂停执行并返回一个值 task coroutine_func(int n 1) {int i 0;while(in){co_yield i;} } 或者用关键词 co_return 完成执行并返回一个值或void task coroutine_func(int n 1) {co_return; } 二、c20协程库 这些关键词都做了啥事情呢。在c20协程库中提供了以下支持库来实现协程 协程特征定义于头文件 coroutine coroutine_traits (C20)用于发现协程承诺类型的特征类型(类模板) 协程柄定义于头文件 coroutine coroutine_handle (C20)用于指代暂停或执行的协程(类模板) 无操作协程定义于头文件 coroutine noop_coroutine (C20)创建在等待或销毁时无可观察作用的协程柄(函数) noop_coroutine_promise (C20)用于无可观察作用的协程(类) noop_coroutine_handle (C20)std::coroutine_handlestd::noop_coroutine_promise 有意用于指代无操作协程 (typedef) 平凡可等待体定义于头文件 coroutine suspend_never (C20)指示 await 表达式应该决不暂停(类) suspend_always (C20)指示 await 表达式应该始终暂停(类) std::coroutine_traits从协程的返回类型与形参类型确定承诺类型。 //定义于头文件 coroutine ,(C20 起) template class R, class... Args struct coroutine_traits;/*模板形参 *R - 协程的返回类型 *Args - 协程的形参类型若协程为非静态成员函数则包括隐式对象形参 */ 标准库实现提供与 R::promise_type 相同的公开可访问成员类型 promise_type 若该有限定标识合法并代表类型。否则它无成员。coroutine_traits 的程序定义特化应当定义公开可访问的成员类型 promise_type 否则行为未定义。 //成员类型 //类型 定义 promise_type R::promise_type //若它合法或由程序定义特化提供 2.1 coroutine_handle句柄 其中最主要的就是coroutine_handle句柄类模板 coroutine_handle 能用于指代暂停或执行的协程定义于头文件 coroutine //(C20 起) /*结构体主模板可从 Promise 类型的承诺对象创建。*/ template class Promise void struct coroutine_handle; /*特化std::coroutine_handlevoid擦除承诺类型。它可从其他特化转换* template struct coroutine_handlevoid; /*特化std::coroutine_handlestd::noop_coroutine_promise指代无操作协程。不能从承诺对象创建它*/ template struct coroutine_handlestd::noop_coroutine_promise; using noop_coroutine_handle std::coroutine_handlestd::noop_coroutine_promise; std::coroutine_handle 的每个特化均为可平凡复制 (TriviallyCopyable) 并保有一个指向协程状态的指针作为其仅有的非静态成员。添加 coroutine_handle 的特化的程序行为未定义。std::coroutine_handle 功能如下 //(C20) 成员函数 (构造函数) 构造 coroutine_handle 对象(公开成员函数) operator 赋值 coroutine_handle 对象(公开成员函数) from_promise [静态]从协程的承诺对象创建 coroutine_handle(公开静态成员函数) 转换 operator coroutine_handle 获得擦除类型的 coroutine_handle(公开成员函数) 观察器 done 检查协程是否已完成(公开成员函数) operator bool 检查柄是否表示协程(公开成员函数) 控制 operator() resume 恢复协程执行(公开成员函数) destroy 销毁协程(公开成员函数) 承诺访问 promise 访问协程的承诺对象(公开成员函数) 导出/导入 address 导出底层地址即支撑协程的指针(公开成员函数) from_address [静态]从指针导入协程(公开静态成员函数) 非成员函数 operator 比较二个 coroutine_handle 对象(函数) operator 比较二个 coroutine_handle 对象(函数) 辅助类 std::hashstd::coroutine_handle std::coroutine_handle 的散列支持(类模板特化) std::coroutine_handle协程句柄是从协程外部操纵的这是用于恢复协程执行或销毁协程帧的非拥有柄承诺promise对象从协程内部操纵协程通过此对象提交其结果或异常。 2.2 std::coroutine_handle实现案例 现在来看如何通过std::coroutine_handle实现协程函数的下面定义一个简单的协程例子 //test0.h #ifndef _TEST_0_H_ #define _TEST_0_H_ void coroutine_first_test(void); #endif //_TEST_0_H_ //test0.cpp #include test0.h#include coroutine #include iostreamstruct task {struct promise_type {task get_return_object() { std::cout task::promise_type.get_return_object \n;return task{Handle::from_promise(*this)}; }//返回std::suspend_never(这个随后说明) 初始化后就继续运行std::suspend_never initial_suspend() { std::cout task::promise_type.initial_suspend \n;return {}; }std::suspend_never final_suspend() noexcept { std::cout task::promise_type.final_suspend \n;return {}; }std::suspend_always yield_value(const int val) noexcept { //co_yield调用std::cout task::promise_type.yield_value val \n;return {};}void return_void() {} //co_return调用void unhandled_exception() {std::cout task::promise_type.unhandled_exception \n;}};using Handle std::coroutine_handlepromise_type;//协程句柄explicit task(Handle coroutine) : m_coroutine{coroutine} {} //get_return_object时调用task() default;~task() { std::cout ~task \n;if (m_coroutine) //自行销毁{m_coroutine.destroy(); }}// task(const task) delete;// task operator(const task) delete;Handle m_coroutine; //协程句柄 };task coroutine_func(int n 0) {int i 0;while(in){co_yield i;std::cout coroutine dosomthing i \n;}co_return; }void coroutine_first_test(void) {auto c0_obj coroutine_func(10);for (size_t i 0; i 5; i){c0_obj.m_coroutine.resume();//唤醒协程std::cout caller dosomthing i \n;} }; //main.cpp #include test0.hint main(int argc, char* argv[]) {coroutine_first_test();return 0; };task是个自定义的结构体为了能作为协程的返回值需要定义一个内部 promise_type结构体。 【1】协程开始执行时它进行下列操作 用 operator new 分配协程状态对象coroutine state。将所有函数形参复制到协程状态中按值传递的形参被移动或复制按引用传递的参数保持为引用因此如果在被指代对象的生存期结束后恢复协程它可能变成悬垂引用上述例如传入的是int型引用。调用承诺对象的构造函数promise。如果承诺类型拥有接收所有协程形参的构造函数那么以复制后的协程实参调用该构造函数。否则调用其默认构造函数。这里采用默认构造函数没具名给出。调用 promise.get_return_object() 并将其结果在局部变量中保持。该调用的结果将在协程首次暂停时返回给调用方。至此并包含这个步骤为止任何抛出的异常均传播回调用方而非置于承诺中。调用 promise.initial_suspend() 。典型的承诺类型要么对于惰性启动的协程返回 std::suspend_always要么对于急切启动的协程返回 std::suspend_never。*如果返回std::suspend_never表示await表达式应该决不暂停立即执行不挂起        *返回std::suspend_always表示await表达式应该始终暂停不立即执行先挂起 当 co_await promise.initial_suspend() 恢复时开始协程体的执行。【2】当协程抵达暂停点时 将先前获得的返回对象返回给调用方/恢复方这里是coroutine_first_test函数如果需要则先隐式转换到协程的返回类型。协程从暂停点返回通过co_yield本质上是调用了promise.yield_value(表达式)调用方/恢复方,coroutine_first_test函数通过调用coroutine_handle的resume告知协程恢复执行协程函数coroutine_func重新从co_yield语句的下一句开始执行。【3】当协程抵达 co_return 语句时它进行下列操作 若是co_return调用 promise.return_void()如果承诺类型 Promise 没有 Promise::return_void() 成员函数本例所采用那么则行为未定义。若是co_return expr调用 promise.return_value(expr)其中 expr 具有非 void 类型或 void 类型如果承诺类型 Promise 没有 Promise::return_value() 成员函数本例子没定义那么则行为未定义。控制流出返回时协程结束开始结束运行。此时以创建的逆序销毁所有具有自动存储期的变量。调用 promise.final_suspend() 。【4】如果协程因未捕捉的异常结束那么它进行下列操作 捕捉异常并在 catch 块内调用 promise.unhandled_exception()调用 promise.final_suspend() 例如以恢复某个继续或发布其结果。此时开始恢复协程是未定义行为。【5】协程当经由 co_return 或未捕捉异常而正常终止它进行下列操作 调用承诺对象的析构函数~promise_type默认析构。调用各个函数形参副本的析构函数本例只有int型引用。调用 operator delete 以释放协程状态所用的内存~task。转移执行回到调用方/恢复方coroutine_first_test。这里的协程函数返回对象采用的是 std::suspend_never等待体标准库定义了两个平凡的可等待体std::suspend_always 及 std::suspend_never先说std::suspend_never //std::suspend_never,定义于头文件 coroutine, //suspend_never 是空类能用于指示 await 表达式绝不暂停并且不产生值。/*成员函数*/ /*(C20 起) *std::suspend_never::await_ready,指示 await 表达式绝不暂停(公开成员函数) *始终返回 true 指示 await 表达式绝不暂停。 */ constexpr bool await_ready() const noexcept { return true; }/*(C20 起) *std::suspend_never::await_suspend,无操作(公开成员函数) *不做任何事。 */ constexpr void await_suspend() const noexcept {}/*(C20 起) *std::suspend_never::await_resume,无操作(公开成员函数) *不做任何事。若使用 suspend_never 则 await 表达式不产生值。 */ constexpr void await_resume() const noexcept {} 编译g main.cpp test*.cpp -o test.exe -stdc20运行程序很好展示了上述逻辑过程 2.3 承诺类型Promise 承诺类型Promise获得到承诺对象的引用。若 *this 不指代承诺对象尚未被销毁的协程则行为未定义。此函数不对特化 std::coroutine_handle 提供。 //std::coroutine_handlePromise::promise //主模板的成员 Promise promise() const; //特化 std::coroutine_handlestd::noop_coroutine_promise 的成员 std::noop_coroutine_promise promise() const noexcept; 编译器用 std::coroutine_traits 从协程的返回类型确定承诺类型。 正式而言如果定义它为非静态成员函数以如下方式确定它的承诺类型 /* *令 R 与 Args... 分别代表协程的返回类型与参数类型列表 *ClassT 与 /*cv限定*/ 如果存在分别代表协程所属的类与其 cv 限定 */ std::coroutine_traitsR, Args...::promise_type //如果不定义协程为非静态成员函数。 std::coroutine_traitsR, ClassT /*cv限定*/, Args...::promise_type //如果定义协程为非右值引用限定的非静态成员函数。 ◦std::coroutine_traitsR, ClassT /*cv限定*/, Args...::promise_type //如果定义协程为右值引用限定的非静态成员函数。 例如如果上述结构体task定义为结构体模板,templatetypename T  struct task其协程函数定义: //如果定义协程为 taskfloat foo(std::string x, bool flag); //那么它的承诺类型是 std::coroutine_traitstaskfloat, std::string, bool::promise_type。//如果定义协程为 taskvoid my_class::method1(int x) const; //那么它的承诺类型是 std::coroutine_traitstaskvoid, const my_class, int::promise_type。//如果定义协程为 taskvoid my_class::method1(int x) ; //那么它的承诺类型是 std::coroutine_traitstaskvoid, my_class, int::promise_type。 每个协程均与下列对象关联 承诺promise对象。协程句柄 (coroutine handle)。协程状态 (coroutine state)它是一个包含以下各项的分配于堆除非优化掉其分配的内部对象 承诺对象各个形参全部按值复制当前暂停点的某种表示使得恢复时程序知晓要从何处继续销毁时知晓有哪些局部变量在作用域内生存期跨过当前暂停点的局部变量和临时量协程状态由非数组 operator new 在堆上分配。如果承诺类型定义了类级别的替代函数那么会使用它否则会使用全局的 operator new如果承诺类型定义了接收额外形参的 operator new 的布置形式且它们所匹配的实参列表中的第一实参是要求的大小std::size_t 类型而其余则是各个协程函数实参那么将这些实参传递给 operator new这使得能对协程使用前导分配器约定 如果分配失败那么协程抛出 std::bad_alloc除非承诺类型 Promise 类型定义了成员函数 Promise::get_return_object_on_allocation_failure()。如果定义了该成员函数那么使用 operator new 的 nothrow 形式进行分配而在分配失败时协程会立即将从 Promise::get_return_object_on_allocation_failure() 获得的对象返回给调用方。 2.4 承诺类型-协程返回类型及协程函数的交互 下来看一下一个更复杂的例子可以从协程传递 co_yield、co_return回引用实现协程与调用者的交互。 //test1.h #ifndef _TEST_1_H_ #define _TEST_1_H_ void coroutine_model_test(void); #endif //_TEST_1_H_ //test1.cpp #include test1.h#include coroutine #include iostream #include optional #include rangestemplatetypename T requires std::movableT class Task { public://promise_type就是承诺对象承诺对象用于协程内外交流struct promise_type {//生成协程返回,会在协程正在运行前进行调用TaskT get_return_object() {std::cout get_return_object \n;return Task{Handle::from_promise(*this)};}/**返回的就是std::suspend_always,在协程被创建及真正运行前被调用*/static std::suspend_always initial_suspend() noexcept {std::cout initial_suspend \n;return {}; }//返回awaiter,在协程最后退出后调用的接口。static std::suspend_always final_suspend() noexcept { std::cout final_suspend \n;return {}; }//返回awaiter,会在 co_yield v 时被调用类型就是Tv就是传入参数valuestd::suspend_always yield_value(T value) noexcept //-4-{current_value std::move(value);std::cout yield_value ;return {};}//会在 co_return v 时被调用把 co_return 后面跟着的值value作为参数传入这里一般就是把这个值保存下来提供给协程调用者void return_value(const T value){std::cout return_value call value \n;//cout危险操作,取决于T类型,这里为了展示原理current_value std::move(value);return;}//会在 co_return v 时被调用,无传入值,和return_value只选一个否则会报编译错误/*void return_void(){std::cout return void invoked. std::endl;}*/// 生成器协程中不允许 co_await 。void await_transform() delete;//协程内的代码抛出了异常这个接口会被调用static void unhandled_exception() {std::cout unhandled_exception ;throw;}std::optionalT current_value;};using Handle std::coroutine_handlepromise_type;//协程句柄explicit Task(Handle coroutine) : m_coroutine{coroutine}{}Task() default;~Task() { std::cout ~Task \n;if (m_coroutine) //自行销毁{m_coroutine.destroy(); }}Task(const Task) delete;Task operator(const Task) delete;Task(Task other) noexcept : m_coroutine{other.m_coroutine}{ other.m_coroutine {}; }Task operator(Task other) noexcept {if (this ! other) {m_coroutine other.m_coroutine;other.m_coroutine {};}return *this;}// 基于范围的 for 循环支持。通过迭代操作实现协程应用class Iter {public:void operator() //-6-{ std::cout real resume ;m_coroutine.resume(); //时恢复协程}const T operator*() const { return *m_coroutine.promise().current_value; //取值通过promise获取数据返回值T} bool operator(std::default_sentinel_t) const { return !m_coroutine || m_coroutine.done(); //赋值时,协程执行}explicit Iter(Handle coroutine) : m_coroutine{coroutine}{}private:Handle m_coroutine;//协程句柄};//range应用指定的开闭区间Iter begin() {if (m_coroutine) {m_coroutine.resume();} return Iter{m_coroutine};}std::default_sentinel_t end() { return {}; }const T get_val(){return *m_coroutine.promise().current_value;} private:Handle m_coroutine; //协程句柄 };//协程函数的返回值为TaskT类型,协程的返回类型必须内部定义TaskT::promise_type templatestd::integral T TaskT range(T first, T last) {T sum T();while (first last) //-2-{sum first;co_yield first;//协程会挂起,返回值;等价于co_await promise.yield_value(表达式)。-3-//调用者resume时,在此处恢复执行std::cout co_yield\n;//-7-}co_return sum;//等价于co_await promise.return_value(表达式) };void coroutine_model_test(void) {auto rs range(-4, 4);for (int i : rs) //-1-,// for (int i : range(-4, 4)) //-1-,{std::cout i ;//-5-}std::cout \n;std::cout rs last val rs.get_val() \n; }; //main.cpp #include test1.hint main(int argc, char* argv[]) {coroutine_model_test();return 0; }; 本案例定义了一个for循环的范围返回函数该范围是Task类的begin()和end()函数提供而函数引用了内置类型IterIter在迭代递增时operator()会调用std::coroutine_handle的resume进行协程恢复。协程运行到“co_yield first;”时就会通过yield_value设置了promise内部的缓存值并返回而调用者函数coroutine_model_test则通过std::coroutine_handle句柄获知promise承诺对象及内部值及遍历数值在遍历时每次递增本质上会调用std::coroutine_handle的resume告知协程恢复执行而协程每次进行递增数值会写入promise承诺对象内部如此反复等同于coroutine_model_test获得遍历范围值。 协程最终返回时“co_return sum;”传递回来一个数值本质上是通过promise承诺对象内部return_value函数实现的。因为通过“current_value std::move(value);”将最后传递进入的值保存在promise承诺对象内部因此在调用函数内通过get_val就能取得该缓存的值。 const T TaskT::get_val(){return *m_coroutine.promise().current_value;} 上述例子中通过“-*-”标识了协程调用逻辑次序编译g main.cpp test*.cpp -o test.exe -stdc20,运行测试 Task内部promise 类的工作主要是两个 从协程的承诺对象创建 coroutine_handle接口是get_return_object。是定义协程的执行流程主要接口是initial_suspendfinal_suspend。是负责协程和调用者之间的数据传递主要接口是 yield_value 和return_value或return_void。通常promise_type类型需要主要实现这几个接口 【1】TaskT get_return_object () 这个接口要能用 promise 自己的实例构造出一个协程的返回值会在协程正在运行前进行调用这个接口的返回值会作为协程的返回值。 【2】std::suspend_always initial_suspend () 这个接口会在协程被创建也就是第一次调用真正运行前被调用。在上述这个例子里指定返回空类指示 await 表达式始终暂停并且不产生值。 return {}; std::suspend_always是一个结构体前面已经说明了std::suspend_never下来看看std::suspend_always它和std::suspend_never几乎一样: /* (C20 起) *std::suspend_always定义于头文件 coroutine *suspend_always 是空类能用于指示 await 表达式始终暂停并且不产生值。 */ struct suspend_always; 该类包含几个成员函数用来判定 成员函数 /*(C20) 指示 await 表达式始终暂停(公开成员函数) *std::suspend_always::await_ready *始终返回 false 指示 await 表达式始终暂停。 */ constexpr bool await_ready() const noexcept { return false; }/*(C20) 无操作(公开成员函数) *std::suspend_always::await_suspend *不做任何事。 */ constexpr void await_suspend() const noexcept {}(C20 起) /*(C20) 无操作(公开成员函数) *std::suspend_always::await_resume *不做任何事。若使用 suspend_always 则 await 表达式不产生值。 */ constexpr void await_resume() const noexcept {} 【3】std::suspend_always yield_value (T v) 这个接口会在 co_yield v 时被调用把 co_yield 后面跟着的值 v 做为参数传入这里一般就是把这个值保存下来提供给协程的调用者返回值一般是std::suspend_always {}。         【4】void return_value (T v) 这个接口会在 co_return v 时被调用把 co_return 后面跟着的值 v 作为参数传入这里一般就是把这个值保存下来提供给协程调用者。         【5】void return_void () 如果 co_return 后面没有接任何值那么就会调用这个接口。return_void 和return_value 只能选择一个实现否则会报编译错误。         【6】std::suspend_always final_suspend () 在协程最后退出后调用的接口如果返回 std::suspend_always 。         【7】协程结束后则需要用户自行调用 coroutine_handle 的 destroy 接口来释放协程相关的资源。若协程对应的 handle 就已经为空不能再调用 destroy 了 (会 coredump)。 【8】void unhandled_exception () 如果协程内的代码抛出了异常那么这个接口会被调用。 std::coroutine_handlepromise_type 是协程的控制句柄类也是协程函数返回类型的最重要成员通过标准库里std::coroutine_handle结构体定义就可以实现与承诺对象的交互能力。恢复协程、销毁协程实例等都是通过该句柄实现。 2.5 co_await 一元运算符 co_await 暂停协程并将控制返回给调用方。它的操作数是一个函数表达式 co_await 函数表达式 函数表达式即函数其返回一个类似于std::suspend_always可等待结构体awaitable就是需要像std::suspend_always一样为该结构体定义await_ready、await_suspend、await_resume函数 协程函数resuming_on_new_thread调用函数表达式funswitch_to_new_threadfun返回结果就是一个等待体这里是awaitable。开始调用 await_ready()。如果它的结果按语境转换成 bool 为 false那么暂停协程以各局部变量和当前暂停点填充其协程状态然后调用 await_suspend 接口并将协程的句柄传给这个接口。如果await_ready 返回 true那么协程完全不会被挂起直接会去调用 await_resume () 接口把这个接口作为 await 的返回值继续执行协程。调用 await_suspend(handle)其中 handle 是表示当前协程的协程句柄。这个函数内部可以通过这个句柄观察暂停的协程而且此函数负责调度它以在某个执行器上恢复或将其销毁并返回 false 当做调度 ◦如果 await_suspend 返回 void那么立即将控制返回给当前协程的调用方/恢复方此协程保持暂停否则如果 await_suspend 返回 bool那么 值为 true 时将控制返回给当前协程的调用方/恢复方值为 false 时恢复当前协程。如果 await_suspend 返回某个其他协程的协程句柄那么通过调用 handle.resume()恢复该句柄注意这可以连锁进行并最终导致当前协程恢复如果 await_suspend 抛出异常那么捕捉该异常恢复协程并立即重抛异常最后调用 await_resume()它的结果就是整个 co_await expr 表达式的结果。如果协程在 co_await 表达式中暂停而在后来恢复那么恢复点处于紧接对 await_resume() 的调用之前。注意因为协程在进入 await_suspend() 前已经完全暂停所以该函数可以自由地在线程间转移协程柄而无需额外同步。例如可以将它放入回调将它调度成在异步 I/O 操作完成时在线程池上运行等。此时因为当前协程可能已被恢复从而执行了等待器的析构函数同时由于 await_suspend() 在当前线程上持续执行await_suspend() 应该把 *this 当作已被销毁并且在柄被发布到其他线程后不再访问它。 2.6 co_await案例 下面例子定义了一个awaitable具有定义await_ready、await_suspend、await_resume函数成员函数通过switch_to_new_thread函数表达式返回co_await。 //test2.h #ifndef _TEST_2_H_ #define _TEST_2_H_ void coroutine_wait_test(void); #endif //_TEST_2_H_ //test2.cpp #include test2.h #include coroutine #include iostream #include stdexcept #include threadauto switch_to_new_thread(std::jthread out) {struct awaitable {std::jthread* p_out;//co_await开始会调用根据返回值决定是否挂起协程bool await_ready() { return false; }//在协程挂起后会调用这个如果返回true会返回调用者如果返回false会立刻resume协程void await_suspend(std::coroutine_handle h) {std::jthread out *p_out;if (out.joinable())throw std::runtime_error(jthread out arg is unull);out std::jthread([h] { h.resume(); });//新建线程并协程恢复std::cout new thread ID: out.get_id() \n; //}//在协程resume的时候会调用这个这个的返回值会作为await的返回值void await_resume() {}};return awaitable{out}; }struct Task{struct promise_type {Task get_return_object() { return {}; }std::suspend_never initial_suspend() { return {}; }std::suspend_never final_suspend() noexcept { return {}; }void return_void() {}void unhandled_exception() {}}; };Task resuming_on_new_thread(std::jthread out) {std::cout initial,ID: std::this_thread::get_id() \n;co_await switch_to_new_thread(out);//协程等待// 等待器在此销毁std::cout final,ID: std::this_thread::get_id() \n; }void coroutine_wait_test(void) {std::jthread out;auto ret resuming_on_new_thread(out); }; //main.cpp #include test2.hint main(int argc, char* argv[]) {coroutine_wait_test();return 0; }; 编译g main.cpp test*.cpp -o test.exe -stdc20及运行程序 2.7 无操作协程 前面描述协程支持库就提到过无操作协程相比一般协程它体现如此特征在协程控制流外不做任何事在开始和恢复后立即暂停拥有一种协程状态而销毁该状态为无操作若有任何指代它的 std::coroutine_handle 则绝不抵达其最终暂停点。 无操作协程定义于头文件 coroutine noop_coroutine (C20)创建在等待或销毁时无可观察作用的协程柄(函数) noop_coroutine_promise (C20)用于无可观察作用的协程(类) noop_coroutine_handle (C20)std::coroutine_handlestd::noop_coroutine_promise 有意用于指代无操作协程 (typedef) std::noop_coroutine_promise是是无操作协程的承诺类型本质上就是一个前面讲述的空promise_type结构体 //定义于头文件 coroutine struct noop_coroutine_promise {}; 而std::noop_coroutine_handle就是std::coroutine_handle句柄以std::noop_coroutine_promise为承诺对象的特例化 //定义于头文件 coroutine,(C20 起) template class Promise void struct coroutine_handle;template struct coroutine_handlestd::noop_coroutine_promise; using noop_coroutine_handle std::coroutine_handlestd::noop_coroutine_promise; std::noop_coroutine是一个函数用来返回指代无操作协程的协程柄。 /*std::noop_coroutine,定义于头文件 coroutine,(C20 起) *返回值指代无操作协程的 std::noop_coroutine_handle *若已有无操作协程的协程状态则不指定 noop_coroutine 的后续调用是返回先前获得的协程柄 *还是指代新的无操作协程的协程状态的协程柄。 */ std::noop_coroutine_handle noop_coroutine() noexcept; 2.8 无操作协程案例 这是协程函数嵌套的例子该例子里协程函数test调用了协程函数get_random它们的返回值都是Taskint。协程返回类型内定义了一个可等待体awaiter在co_await调用时开始触发。另外还为协程返回类型Task定义了承诺类型promise_type及在该承诺类型内定义了一个可等待体final_awaiter它会在承诺类型调用final_suspend时构建和开发触发。可等待体final_awaiter的await_suspend函数在传递协程句柄有效时直接返回以恢复先前的协程否则返回 noop_coroutine() 其恢复不做任何事。 //test3.h #ifndef _TEST_3_H_ #define _TEST_3_H_void coroutine_noop_test(void);#endif //_TEST_3_H_ //test3.cpp #include test3.h #include coroutine #include utility #include iostreamtemplateclass T struct Task {struct promise_type {//承诺类型promise_type() : result(T()),previous(std::noop_coroutine()){std::cout in Task::promise_type()\n;};auto get_return_object() {std::cout in Task::promise_type::get_return_object()\n;return Task(std::coroutine_handlepromise_type::from_promise(*this));}//返回std::suspend_always{} 表示await表达式应该始终暂停不立即执行先挂起std::suspend_always initial_suspend() { std::cout in Task::promise_type::initial_suspend()\n;return {}; }//可等待体定义struct final_awaiter {//co_await开始会调用根据返回值决定是否挂起协程bool await_ready() noexcept(true) { std::cout in Task::promise_type::final_awaiter::await_ready()\n;return false; }//在协程挂起后会调用这个如果返回true会返回调用者如果返回false会立刻resume协程std::coroutine_handle await_suspend(std::coroutine_handlepromise_type h) noexcept(true){// 在当前协程以 h 指代执行即将结束时调用 final_awaiter::await_suspend 。// 若当前协程被另一协程经由 co_await get_Task() 恢复则存储到该协程的柄// 为 h.promise().previous 。该情况下返回柄以恢复先前的协程。// 否则返回 noop_coroutine() 其恢复不做任何事。std::cout in Task::promise_type::final_awaiter::await_suspend()\n;std::cout T h.promise().result \n;//co_return *传递的值不规范语句主要为了测试逻辑展示auto previous h.promise().previous;if (previous) {return previous;} else {return std::noop_coroutine();}}//在协程resume的时候会调用这个这个的返回值会作为final_awaiter的返回值void await_resume() noexcept(true) {std::cout in Task::promise_type::final_awaiter::await_resume()\n;}};//返回final_awaiter{},Task结束协程时,将进入promise_type.final_suspend,进入final_awaiter等待体执行逻辑final_awaiter final_suspend() noexcept { std::cout in Task::promise_type::final_suspend()\n;return {}; }void unhandled_exception() { throw; }//会在 co_return v 时被调用把这个v值保存下来提供给协程调用者void return_value(T value) { std::cout in Task::promise_type::return_value()\n;result std::move(value); }T result;std::coroutine_handle previous;};//Task(std::coroutine_handlepromise_type h) : coro(h) {//get_return_object函数内调用std::cout in Task()\n;}Task(Task t) delete;~Task() { std::cout in ~Task()\n;coro.destroy(); }//可等待体定义struct awaiter {//co_await开始会调用根据返回值决定是否挂起协程bool await_ready() { std::cout in Task::awaiter::await_ready()\n;return false; //挂起}//在协程挂起后会调用这个如果返回true会返回调用者如果返回false会立刻resume协程auto await_suspend(std::coroutine_handle h) {std::cout in Task::awaiter::await_suspend()\n;coro.promise().previous h;//将Task协程句柄指向的promise其内部定义std::coroutine_handle 特例化句柄return coro;}//在协程resume的时候会调用这个这个的返回值会作为awaiter的返回值T await_resume() { std::cout in Task::awaiter::await_resume()\n;return std::move(coro.promise().result); }std::coroutine_handlepromise_type coro;};awaiter operator co_await() { //co_await调用std::cout in Task::co_await()\n;return awaiter{coro}; //将Task协程句柄传入}T operator()() {std::cout in Task::operator()\n;coro.resume();return std::move(coro.promise().result);} private:std::coroutine_handlepromise_type coro;//协程句柄 };//协程函数返回Taskint Taskint get_random() {std::cout in get_random()\n;co_return 4; };//协程函数返回Taskint Taskint test() {Taskint v get_random(); //Taskint u get_random();std::cout in test()\n;int x (co_await v co_await u);//相当于调用Task::co_await()co_return x; };void coroutine_noop_test(void) {Taskint t test();//int result t();std::cout result \n; }; //main.cpp #include test3.hint main(int argc, char* argv[]) {coroutine_noop_test();return 0; };编译g main.cpp test3.cpp -o test.exe -stdc20运行测试
http://www.dnsts.com.cn/news/154712.html

相关文章:

  • 51做网站个人怎么做旅游网站
  • 有没有可以在线做化学实验的网站免费咨询律师网站
  • 网站建设 官seo网站代码优化
  • 昌平区网站建设公司请列举常见的网站推广方法
  • 推广网站怎么做建设工程信息官网查询系统
  • 购物网站案例驾考学时在哪个网站做
  • 网站分析与优化的文章广州一起做网站
  • 徐州模板开发建站列表页面设计模板
  • 河南网站推广优化多少钱wordpress默认固定链接
  • 自己的网站怎么做优化个人音乐网站程序
  • 营销型网站的建设要求都有什么作用炫酷手机网站模板
  • 建一个网站的流程c语言做网站
  • 同字形结构布局网站天津模板建站代理
  • 网站建设合作网站设计风格有哪几种
  • 苏州网站推广排名建设网络平台的技术服务合同交印花税吗
  • wamp 怎么做两个网站南通优化网站收费标准
  • 智能建站免费广东贸易网站建设
  • 甘肃省住房和城乡建设局网站在线观看视频的免费网站
  • 前端网站开发实例怎么样让百度搜到自己的网站
  • 阿里云建站后台中国职业培训在线
  • 百度恶意屏蔽网站网店美工课程总结
  • 响应式网站开发框架重庆建设工程信息网三类人员
  • 海宁市住房与城乡规划建设局网站网站排名下降的原因
  • 网站基本配置国内做彩票网站违法么
  • 投票网站定制阜宁做网站哪家好
  • 深圳网站建设需要多少钱网站制作的大公司
  • 山东规划 建设部门的网站wordpress文章专题插件
  • 昆明做网站建设企业推荐联盟网站做的最好
  • 上市公司做网站有什么用最新新闻热点300字
  • seo是什么seo怎么做重庆seo网站排名