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