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 #include #include #include #include template 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 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;  //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 1MyTask 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");}#endifMyTask 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;}

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。