C++20 协程体验

Posted 鲜花盔甲的主人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++20 协程体验相关的知识,希望对你有一定的参考价值。

1 介绍

  • 协程是比线程更加轻量级并发编程方式,CPU资源在用户态进行切换,CPU切换信息在用户态保存。

  • 协程完成异步的调用流程,并对用户展示出同步的使用方式。

  • 协程的调度由应用层决定,所以不同的实现会有不同的调度方式,调度策略比较灵活。

  • 协程是基于线程之上运行,同一个线程中,协程是串行的,不会产生线程资源的竞争,不同的协程间却是相互交叉运行的,只要依赖的线程没有终止,协程最终会跳转回来。

  • 协程可以充分利用单核CPU的资源,但是不太好利用多核CPU资源。

  • c++20 协程使用三大关键字 co_wait,co_return,co_yield

  • 在函数中使用到以上关键字的函数被称为协程函数,并且通过该关键字完成跳转。

2 使用

如果要使用协程函数,需要定义promise_type以及基本成员函数实现。

包括get_return_object、initial_suspend、final_suspend、unhandled_exception。

演示代码最下面展示

co_return 执行完协程函数并返回结果

需要额外定义return_void函数。

流程分析:

1 可以看出调用co_return跳转到return_void,return_void执行完后,main函数向下执行。

2 "co_test1 end"并没有打印,说明协程函数co_test1分割开来,通过co_return切换了CPU资源,使主线程继续执行。

co_await 执行到异步操作处,判断并进行挂起操作。

使用co_await 还需要再定义xxxx类并实现await_ready、await_suspend、await_resume函数。

1 协程函数中调用co_await后,跳转xxx的await_ready并判断是否就绪,如果是true,则协程函数调回继续运行,反之进入await_suspend挂起,协程函数跳出,直到调用await_resume后再次跳入协程函数执行余下操作。

2 可以看到在“co_test2 result”打印之前,main函数已执行完成,等到await_resume后依然会跳回协程函数并执行余下部分。

co_yield 让出操作

需要额外定义yield_value函数。

1 执行co_yield 会跳转到yield_value函数中,通过resume以及promise操作获取结果。

3 代码用例

以下代码在linux下测试,gcc版本需要 linux-gcc10.1以上。

编译指令:g++ faw.cpp -fcoroutines -std=c++20

#include <iostream>
#include <coroutine>
#include <thread>
#include <functional>
#include <chrono>

template <typename... Args>
void print_log(const char* fmt, Args... args) 
    char log_buf[128] =  0 ;
    snprintf(log_buf, 128, fmt, args...);
    char time_buf[64] =  0 ;
    unsigned long tid = pthread_self();
    char buf[160] =  0 ;
    snprintf(buf, 160, "[%lu] [%s]", tid, log_buf);
    std::cout << buf << std::endl;


using callback_t = std::function<void(int)>;
void async_op(int value, callback_t cb) 
      std::thread t([value, cb]() 
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            cb(value+1000);
      );
      t.detach();


struct MyTask 
      struct promise_type;
      using handle_t = std::coroutine_handle<promise_type>;  //yield操作
      MyTask() 
      
      MyTask(handle_t handle)
            : handle_(handle) 
      
      struct promise_type 
            MyTask get_return_object() 
                  print_log("get_return_object beg");
                  return MyTask(handle_t::from_promise(*this));
            
            std::suspend_never initial_suspend() 
                  print_log("initial_suspend beg");
                  return std::suspend_never();
            
            std::suspend_never final_suspend() noexcept 
                  print_log("final_suspend beg");
                  return std::suspend_never();
            

            //co_return
            void return_void() 
                  print_log("return_void beg");
                  std::this_thread::sleep_for(std::chrono::milliseconds(1000)); //测试异步
            
            void unhandled_exception() 

            //co_yield
            auto yield_value(int v) 
                  data_ = v;
                  return std::suspend_always();
            

            int data_ = 0;
      ;

 int get_value() 
       handle_.resume();
       if(!handle_.done())
            return handle_.promise().data_;
       
       return -1;
 

      handle_t handle_;
;

//co_await操作
class AwaitOp 
public:
      AwaitOp(int value)
            : input_(value), result_(0) 

      bool await_ready() 
            print_log("await_ready beg");
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));  //测试阻塞情况
            return false;
      

      void await_suspend(std::coroutine_handle<> handle) 
            auto cb = [handle, this](int value) mutable 
                  result_ = value;
                  print_log("----------");
                  handle.resume();   //执行完后调回
                  
            ;
            async_op(input_, cb);
      

      int await_resume() 
            print_log("await_resume beg");
            return result_;
      
private:
      int input_;;
      int result_;
;

#if 1
MyTask co_test1()
    print_log("co_test1 beg");
    co_return;   //业务跳转,协程函数退出
    print_log("co_test1 end");


MyTask co_test2()
    print_log("co_test2 beg");
    int input= 999;
    int result = co_await AwaitOp(input);   //业务跳转,协程函数退出
    print_log("co_test2 result=%d",result);
    co_return;
    print_log("co_test2 end");


#endif
MyTask co_test3_2()
    print_log("co_test3_2 beg");
    int t = 99;
    co_yield t;  //切换出去
    print_log("co_test3_2 end");


void co_test3()
      MyTask task = co_test3_2();
      int result = task.get_value();  //切换结果
      print_log("co_test3 result=%d",result);


int main() 
      print_log("main beg");

      co_test1();     //co_return测试
      //co_test2();   //co_await测试
      //co_test3();   //co_yield测试
      print_log("main end");
      getchar();
      return 0;

以上是关于C++20 协程体验的主要内容,如果未能解决你的问题,请参考以下文章

dedecms去掉底部的powered by dedecms 版块信息

无法禁用X-Powered-By:Express

如何移除网站Response Headers中的X-Powered-By信息?

c++20 协程 图片识别框架 紫丁香

c++20 协程本质

X-Powered-By中的Express在哪个地方能改呢