首页 > 编程语言 >C++20 协程:异步编程的新纪元

C++20 协程:异步编程的新纪元

时间:2024-09-09 13:53:31浏览次数:12  
标签:std suspend 20 handle C++ return io 协程

C++20 引入了协程(coroutines),这是一种全新的异步编程模型,使得编写异步代码变得更加简洁和直观。本文将详细介绍 C++20 协程的概念、功能演变及其在实际项目中的应用。通过本文,你将了解到协程的基本原理、语法和如何利用协程来简化异步编程。

1. 协程的概念

协程(coroutine) 是一种可以暂停执行的函数,它可以在执行过程中挂起(yield)并恢复执行。与传统的函数不同,协程可以在执行过程中多次进出,从而实现更高效的异步编程。

2. 功能演变
2.1 早期尝试
  • Boost.Coroutine:Boost 库早在 C++11 之前就提供了协程的支持。它通过底层的上下文切换来实现协程的功能。
  • P0333R11:C++ 标准委员会提出了一个关于协程的设计提案,最终在 C++20 中被采纳。
2.2 C++20 标准
  • co_await:用于等待一个协程表达式的结果。
  • co_yield:用于产生一个值,并暂时挂起协程。
  • co_return:用于从协程返回一个值。
3. C++20 协程的基本语法
3.1 协程函数

协程函数可以通过在返回类型前加上 ->std::suspend_neverstd::suspend_alwaysstd::suspend_never 来声明。

  • std::suspend_never:表示协程不会挂起。
  • std::suspend_always:表示协程总是挂起。
  • std::suspend_also:表示协程可能挂起也可能不挂起。
// 一个简单的协程函数
struct SimpleCoroutine {
    int operator()(int x) -> std::suspend_never {
        co_yield x * 2;
        co_return;
    }
};

int main() {
    SimpleCoroutine coro;
    auto result = coro(5); // 10
}
3.2 协程控制块

协程控制块(coroutine handle)用于管理协程的生命周期。

// 创建协程控制块
struct Coroutine {
    struct promise_type {
        int value;
        
        // 构造函数
        promise_type(int val) : value(val) {}

        // get_return_object
        auto get_return_object() {
            return *this;
        }

        // initial_suspend
        std::suspend_never initial_suspend() {}

        // final_suspend
        std::suspend_never final_suspend() noexcept {}

        // return
        void return_value(int val) {}

        // unhandled_exception
        void unhandled_exception() {}
    };

    coroutine_handle<promise_type> handle;

    Coroutine(coroutine_handle<promise_type> h) : handle(h) {}

    // 启动协程
    void start() {
        if (!handle.done()) {
            handle.resume();
        }
    }

    // 挂起协程
    void suspend() {
        if (!handle.done()) {
            handle.promise().value = 42;
            handle.suspend();
        }
    }

    // 等待协程
    int await() {
        if (!handle.done()) {
            handle.resume();
        }
        return handle.promise().value;
    }
};

// 使用协程
Coroutine::promise_type::value = 10;
Coroutine coro(Coroutine::promise_type::make_promise());
coro.start();
int result = coro.await(); // 42
4. 实战应用
4.1 异步 I/O

协程非常适合用于异步 I/O 操作。下面是一个简单的异步文件读取示例:

#include <iostream>
#include <fstream>
#include <coroutine>

struct AsyncFileReader {
    struct promise_type {
        std::string filename;
        std::ifstream file;
        std::string buffer;

        // get_return_object
        AsyncFileReader get_return_object() {
            return *this;
        }

        // initial_suspend
        std::suspend_never initial_suspend() {}

        // final_suspend
        std::suspend_never final_suspend() noexcept {}

        // return
        void return_value(std::string buf) {
            buffer = std::move(buf);
        }

        // unhandled_exception
        void unhandled_exception() {}

        void read_file() {
            file.open(filename);
            if (file.is_open()) {
                std::getline(file, buffer);
                co_return buffer;
            } else {
                co_return "";
            }
        }
    };

    coroutine_handle<promise_type> handle;

    AsyncFileReader(coroutine_handle<promise_type> h) : handle(h) {}

    std::string await_resume() {
        handle.resume();
        return handle.promise().buffer;
    }
};

AsyncFileReader async_read_file(std::string filename) {
    return AsyncFileReader{coroutine_handle<promise_type>::from_promise(promise_type{filename})};
}

int main() {
    auto result = async_read_file("example.txt").await_resume();
    std::cout << "Read data: " << result << std::endl;
}
4.2 异步网络编程

协程也非常适合用于异步网络编程。下面是一个简单的异步 HTTP 请求示例:

#include <iostream>
#include <coroutine>
#include <asio/awaitable.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/steady_timer.hpp>
#include <asio/co_spawn.hpp>

using namespace asio::awaitable;

struct HttpClient {
    asio::io_context& io;
    asio::ip::tcp::socket socket;

    HttpClient(asio::io_context& io) : io(io), socket(io) {}

    void connect(const std::string& host, const std::string& port) {
        asio::connect(socket, asio::ip::tcp::resolver(io).resolve(host, port));
    }

    std::string send_request(const std::string& request) {
        asio::write(socket, asio::buffer(request));
        asio::streambuf response_buffer;
        asio::read_until(socket, response_buffer, "\r\n\r\n");
        return asio::buffer_cast<const char*>(response_buffer.data());
    }
};

struct AsyncHttpClient {
    asio::io_context& io;
    HttpClient client;

    AsyncHttpClient(asio::io_context& io, const std::string& host, const std::string& port)
        : io(io), client(io) {
        client.connect(host, port);
    }

    std::string async_send_request(const std::string& request) {
        co_await asio::co_spawn(io, [&]() -> awaitable<void> {
            auto response = client.send_request(request);
            co_return;
        }, asio::use_awaitable);

        // 返回结果
        co_return "Response received";
    }
};

int main() {
    asio::io_context io;
    AsyncHttpClient client(io, "example.com", "80");

    auto result = client.async_send_request("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
    std::cout << "Received response: " << result << std::endl;

    io.run();
}

结论

通过本文的介绍,你不仅了解了 C++20 协程的基本概念和语法,还学会了如何在实际项目中应用协程来简化异步编程。协程的引入极大地提升了 C++ 异步编程的能力,使得编写高性能、可维护的异步代码变得更加容易。希望这篇文章对你有所帮助,如果你有任何具体的问题或需要进一步的帮助,请随时留言交流!


通过本文的介绍,你将能够更好地理解和使用 C++20 协程,并在异步编程中有效地管理异步操作,提升应用程序的性能和用户体验。

标签:std,suspend,20,handle,C++,return,io,协程
From: https://blog.csdn.net/jianglq/article/details/142057196

相关文章

  • 什么是网络准入控制系统?2024年7款好用网络准入控制系统推荐
    随着信息技术的飞速发展,网络安全已成为企业和组织不可忽视的重要议题。网络准入控制系统(NetworkAccessControl,NAC)作为网络安全领域的一项关键技术,扮演着至关重要的角色。它类似于网络世界的“门禁系统”,确保只有经过认证、符合安全策略的设备和用户才能接入网络,从而有效防范......
  • D45XT120-ASEMI无人机专用D45XT120
    编辑:llD45XT120-ASEMI无人机专用D45XT120型号:26MT160品牌:ASEMI封装:DXT-5批号:2024+现货:50000+最大重复峰值反向电压:1200V最大正向平均整流电流(Vdss):45A功率(Pd):大功率芯片个数:5引脚数量:5安装方式:直插类型:整流扁桥、整流桥正向浪涌电流:450A正向电压:1.00V~1.10V封装......
  • P2471 [SCOI2007] 降雨量 题解
    题目传送门分析分讨题。首先发现是RMQ问题(区间最值),可以用线段树或ST表来维护(代码为线段树,因为我忘记ST表怎么写了)。然后发现有些年份不明确导致区间判断似乎不好搞。但事实上只要判断下标差是否等于年份差即可得出该区间有无不明确年份。其次考虑“必真”,“必假”,“......
  • GZOI2024 Day1 T2 card
    GZOI2024Day1T2card首先最后一张牌可能不会弃满\(b_i\)张牌。而如果我们要打出若干张牌,肯定想要最后打出\(b_i\)最大的那张牌,这样显然更划算。因此要先按照\(b_i\)排序。首先很容易想到背包。把同类牌拆成\(c_i\)个,然后直接背包:\(f_{i,j}\)表示遍历到第\(i\)张牌,......
  • CS3214 Fall 2024     Exercise
    CS3214Fall2024       Exercise0DueDate: seewebsiteInthisclass,youarerequiredtohavefamiliaritywithUnixcommandsandUnixpro-grammingenvironments.Thefirstpartofthisexerciseisareviewtomakesureyouarecomfortableinou......
  • C++---内存管理
    1C/C++内存分布栈区:由编译器自动分配和释放,存放运行时候的局部变量,函数参数,返回数据,返回地址。堆区:一般由程序员自己分配,然后自己释放,例如栈的实现malloc开辟的数组空间。数据段(静态区):存放全局变量,静态数据,常量,程序结束后自动释放。代码段(常量区):存放常量字符串和可执行代......
  • C++--static成员和友元
    1static声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化静态定义的成员变量在类外定义,变量类型类名::变量名=value的形式。此外,static还可以在类里面定义......
  • 南沙C信++奥赛陈老师解一本通题: 1205:汉诺塔问题
    ​【题目描述】约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到中间的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。这是一个著名的问题,几乎所有的教......
  • 2025秋招NLP算法面试真题(十九)-大模型分布式训练题目
    分布式训练题目1.理论篇1.1训练大语言模型存在问题?计算资源需求**:**训练大型语言模型需要大量的计算资源,包括高端GPU、大量的内存和高速存储器。这可能限制了许多研究人员和组织的训练能力,因为这些资源通常很昂贵。数据需求**:**训练大型语言模型需要大规模的数......
  • 2025秋招NLP算法面试真题(十八)-大模型训练数据格式常见问题
    1.SFT(有监督微调)的数据集格式?对于大语言模型的训练中,SFT(SupervisedFine-Tuning)的数据集格式可以采用以下方式:输入数据:输入数据是一个文本序列,通常是一个句子或者一个段落。每个样本可以是一个字符串或者是一个tokenized的文本序列。标签数据:标签数据是与输入数据对应的......