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;}