C++ Coroutine简单学习教程

C++ Coroutine简单学习教程

协程是一种用户态线程,可以避免线程切换等开销,提高代码效率。C++20引入了支持协程的新关键字co_await、co_yield、co_return等,可以方便地实现协程。本教程将介绍C++协程的基本使用方法。

环境准备

在编译器中开启协程支持,需要使用/await选项。

使用clang++编译示例代码:

clang++ -std=c++20 -fcoroutines-ts -stdlib=libc++ -lc++abi main.cpp

基本语法

协程相关关键字:

  • co_await:等待另一个协程的完成,并将结果返回给调用者
  • co_yield:将控制权暂时交回给调用者,并将一个值返回给调用者
  • co_return:协程结束,并返回结果

协程可以分为两种:生成器和异步函数。

生成器是一种协程,它可以generator_yield返回一个值,而后再次resume时从generator_yield挂起的地方继续执行。

异步函数是一个返回期望对象的函数,期望对象将在异步操作完成时获取值。

使用示例

示例一:生成器

#include <iostream>
#include <experimental/coroutine>

class Generator {
public:
    struct promise_type;
    using CoroutineHandle = std::experimental::coroutine_handle<promise_type>;

    struct promise_type {
        int current_value;

        auto get_return_object() {
            return Generator{CoroutineHandle::from_promise(*this)};
        }

        auto initial_suspend() {
            return std::experimental::suspend_always{};
        }

        auto final_suspend() {
            return std::experimental::suspend_always{};
        }

        void unhandled_exception(){
            std::terminate();
        }

        auto yield_value(int value){
            current_value = value;
            return std::experimental::suspend_always{};
        }
    };

    Generator(CoroutineHandle handle) : coro{handle} {}

    ~Generator() {
        if(coro) {
            coro.destroy();
        }
    }

    int get() {
        coro.resume();
        return coro.promise().current_value;
    }

    bool moveNext()
    {
        if (coro.done())
        {
            return false;           // 生成器已经结束
        }
        coro.resume();              // 继续执行生成器
        return !coro.done();
    }

private:
    CoroutineHandle coro;
};

Generator gen() {
    int i = 0;
    while (true) {
        co_yield i++;
    }
}

int main() {
    auto g = gen();
    for (int i = 0; i < 10; ++i) {
        std::cout << g.get() << "\n";
        g.moveNext();
    }
}

输出:

0
1
2
3
4
5
6
7
8
9

这里就写了一个简单的生成器,每次调用 get() 函数就可以获得生成器的下一个值。生成器在每次挂起的时候,调用者将会得到一个 yield_value 的返回值,并且当生成器结束时,将会在 final_suspend 函数里面清理资源。

示例二:异步函数

#include <iostream>
#include <experimental/coroutine>
#include <chrono>
#include <thread>

std::experimental::coroutine_handle<> async_handle;

struct Async {
    struct promise_type {
        std::experimental::suspend_never get_return_object() {
            return {};
        }
        std::experimental::suspend_always initial_suspend() {
            return {};
        }
        std::experimental::suspend_always final_suspend() noexcept {
            return {};
        }
        void return_void() {}
    };
};

void async_test() {
    async_handle.resume();
}

Async async_sleep(int ms) {
    std::thread t([ms](){
        std::this_thread::sleep_for(std::chrono::milliseconds(ms));
        async_test();
    });
    t.detach();
    co_await async_handle;
}

int main() {
    async_handle = std::experimental::coroutine_handle<>::from_address(nullptr);
    async_sleep(1000); //等待1000ms
    std::cout << "async_sleep complete\n";
}

上述代码中,我们使用了一个异步函数来实现等待一段时间的效果。当我们在 async_sleep 中调用 co_await async_handle 的时候,我们就将当前协程挂起,直到 async_test 函数中再次调用了 async_handle.resume() ,协程才会继续执行。

输出:

async_sleep complete

这种方式相较于线程等待,协程的切换成本更低,可以更加高效地实现异步。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++ Coroutine简单学习教程 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • Android使用jni调用c++/c方法详解

    Android使用Jni调用C++/C方法详解 什么是JNI? JNI全称Java Native Interface,就是Java本地接口,它可以让Java程序调用其他语言编写的动态库,比如C++、C语言等。 Jni调用C++/C方法步骤 准备好动态库。在使用Jni调用C++/C方法之前,首先需要编写好被调用的C++/C代码,并将其编译成动态库。在编译完成后…

    C 2023年5月23日
    00
  • Qt数据库应用之实现数据打印到纸张

    实现数据打印到纸张通常需要使用第三方库或者一些特定的框架,而Qt作为一种优秀的跨平台开发框架,也提供了相关的类和方法来实现数据的打印。下面,我将详细讲解Qt数据库应用之实现数据打印到纸张的完整攻略,其中将会包含两条示例代码演示。 1. 准备工作 在进行打印操作之前,需要进行如下准备工作: 1.1 创建一个Qt应用程序 首先,需要在Qt IDE中创建一个Qt应…

    C 2023年5月22日
    00
  • 浅析ARM架构下的函数的调用过程

    浅析ARM架构下的函数的调用过程 ARM函数调用基本流程 ARM函数调用的基本流程如下: 调用者保存寄存器(Callee saved registers):在调用函数之前,调用者需要保存被调用者需要用到的寄存器,否则这些值会被调用函数所覆盖,导致逻辑错误。在ARM架构中,callee saved registers 都是 r4-r11,他们将被保存在当前堆栈…

    C 2023年5月23日
    00
  • RedHat linux 8.0下内核编译步骤和说明

    RedHat Linux 8.0下内核编译步骤和说明 前置条件 已安装RedHat Linux 8.0操作系统 具备基本的Linux命令行操作技巧 下载Linux内核源码包 步骤说明 步骤1:解压源码包 将下载的Linux内核源码包解压到任意位置,例如/home/username/kernel。 步骤2:配置内核 进入源码目录,使用以下命令进行配置: mak…

    C 2023年5月22日
    00
  • C++实现简单五子棋游戏

    C++实现简单五子棋游戏攻略 简介 五子棋是一种非常经典的棋类游戏,如何用C++实现一个简单的五子棋游戏呢?本篇攻略将为大家提供一份完整的实现方案。 步骤 1. 游戏界面 首先,我们需要设计一个游戏界面。可以考虑使用图形界面库来实现,也可以使用控制台进行文字输出。 示例代码: void printBoard(vector<vector<char&…

    C 2023年5月23日
    00
  • 详解设计模式中的Command命令模式及相关C++实现

    详解设计模式中的Command命令模式及相关C++实现 什么是Command模式? Command模式是一种行为型设计模式,它将请求封装成一个对象,从而使您可以使用不同的请求、队列或日志请求参数化客户端对象。该模式还支持撤销操作。 Command模式的角色 Command模式涉及以下四个角色: Receiver: 程序执行实际操作的对象(比如照明系统、音响设…

    C 2023年5月22日
    00
  • C# XML与Json之间相互转换实例详解

    C# XML与Json之间相互转换实例详解 本文将详细讲解在C#中如何实现XML与Json之间的相互转换。 1. XML转Json实例 首先我们需要引入System.Xml和Newtonsoft.Json两个命名空间,代码如下: using System.Xml; using Newtonsoft.Json; 我们首先需要创建一个XML文档,然后将其转换成J…

    C 2023年5月23日
    00
  • 详解关于JSON.parse()和JSON.stringify()的性能小测试

    关于“详解关于JSON.parse()和JSON.stringify()的性能小测试”攻略,以下是完整的说明: 标题 1. 概述 在JavaScript中,JSON.parse()和JSON.stringify()是两个常用的方法,前者将JSON格式的字符串转换为JavaScript对象,后者则是将JavaScript对象转换为JSON格式的字符串。同时,在…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部