C++并发之协程实例(一)(coroutine, std::coroutine_handle, std::suspend_never, std::suspend_always)

1 协程

  协程(Coroutines)是一个可以挂起执行以便稍后恢复的函数。协程是无堆栈的:它们通过返回到调用方来暂停执行,并且恢复执行所需的数据与堆栈分开存储。这允许异步执行的顺序代码(例如,在没有显式回调的情况下处理非阻塞I/O),还支持惰性计算无限序列上的算法和其他用途。
协程类图如下:
协程类

2 实例

C++20 标准增加了协程,其头文件是coroutine
实例代码如下:

#include <coroutine> //for std::coroutine_handle std::suspend_never
#include <iostream>
#include <stdexcept>
#include <thread>
 
auto switch_to_new_thread(std::jthread& out)
{
    struct awaitable
    {
        std::jthread* p_out;
        bool await_ready() 
        {
            std::cerr << "1.await_ready" << std::endl;
            return false; 
        }
        void await_suspend(std::coroutine_handle<> h)
        {
            std::jthread& out = *p_out;
            if (out.joinable())
                throw std::runtime_error("Output jthread parameter not empty");
            out = std::jthread([h] { h.resume(); });
            std::cerr << "2.await_suspend" << std::endl;
            std::cout << "新线程ID: " << out.get_id() << '\n'; // this is OK
        }
        void await_resume() 
        {
            std::cerr << "3.await_resume" << std::endl;
        }
    };
    return awaitable{&out};
}
 
struct task
{
    struct promise_type
    {
        task get_return_object() {
            std::cerr << "1.get_return_object" << std::endl;
            return {}; 
        }
        std::suspend_never initial_suspend() 
        {
            std::cerr << "2.initial_suspend" << std::endl;
            return {}; 
        }
        std::suspend_never final_suspend() noexcept 
        {
            std::cerr << "4.final_suspend" << std::endl;
            return {}; 
        }
        void return_void()
        {
            std::cerr << "3.return_void" << std::endl;
        }
        void unhandled_exception() 
        {
            std::cerr << "5.unhandled_exception" << std::endl;
        }
    };
};
 
task resuming_on_new_thread(std::jthread& out)
{
    std::cout << "协程在线程上启动: " << std::this_thread::get_id() << '\n';
    co_await switch_to_new_thread(out);
    // awaiter destroyed here
    std::cout << "协程在线程上恢复: " << std::this_thread::get_id() << '\n';
}
 
int main()
{
    std::jthread out;
    resuming_on_new_thread(out);
}

说明:

  • 对于awaitable,必须实现接口:
    • await_ready
    • await_suspend
    • await_resume
  • 对task,必须定义类型promise_type且promise_type必须实现接口:
    • get_return_object
    • initial_suspend
    • final_suspend
    • return_void或return_value或yield_value
    • unhandled_exception

3 编译

$ g++ coroutinines.cpp -o coro
In file included from coroutinines.cpp:1:
/usr/include/c++/10/coroutine:295:2: error: #error "the coroutine header requires -fcoroutines"
  295 | #error "the coroutine header requires -fcoroutines"
      |  ^~~~~
coroutinines.cpp:6:32: error: ‘jthread’ is not a member of ‘std’; did you mean ‘thread’?
    6 | auto switch_to_new_thread(std::jthread& out)

增加编译选项-fcoroutines

$ g++ -fcoroutines coroutinines.cpp -o coro
coroutinines.cpp:6:32: error: ‘jthread’ is not a member of ‘std’; did you mean ‘thread’?
    6 | auto switch_to_new_thread(std::jthread& out)
      |                                ^~~~~~~
      |                                thread

增加编译选项-std=c++20使用C++20标准

g++ -std=c++20 coroutinines.cpp  -fcoroutines -o coro

编译通过.

4 运行

$ ./coro
1.get_return_object
2.initial_suspend
协程在线程上启动: 140662219543040
1.await_ready
2.await_suspend
新线程ID: 140662219408960
3.await_resume
协程在线程上恢复: 140662219408960
3.return_void
4.final_suspend

调用流程:

  • task.get_return_object
  • task.initial_suspend
  • awaitable.await_ready
  • awaitable.await_suspend
  • awaitable.await_resume
  • task.return_void
  • final_suspend

5 总结

  • 新线程ID: 140662219408960 和协程在线程上恢复: 140662219408960的两个线程id相同,说明co_await调用作为一个分界线,之上是一个线程,之下是一个线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysnow010

你的鼓励就是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值