Asio18-Coroutine

Asio18-Coroutine

什么是协程

简单来说,协程是一个可以暂停执行和恢复执行的函数.

  • 普通函数:调用 → 一直执行到 return 或函数结束 → 返回调用点。调用一次,执行一次,执行完毕后就“烟消云散”,所有局部状态都销毁了。
  • 协程:调用 → 执行到某个点可以暂停(挂起) → 将执行权交还给调用者 → 在未来的某个时刻,调用者可以命令协程从刚才暂停的地方恢复执行。

你可以把协程想象成一个可以多次进入和退出的函数,并且每次退出时都能保留当时的“现场”(包括局部变量、执行位置等)。

主要优势

  • 编程方式的改变:将复杂的异步回调代码该写成类似同步的易于理解和维护的线性代码.
  • 轻量:协程运行在用户态的空间,系统并不会感知到所谓的协程,也不存在类似线程切换,进程等的大量的上下文开销.
  • 惰性生成器:高效的生成序列,节省内存.

怎么理解呢?我们以乒乓模型的服务器(echo型)的读写为例子(asio下),看下面这样一段代码:

awaitable<void> echo(boost::asio::ip::tcp::socket socket)
{
    try{
        char data[1024];
        for(;;){
            std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data),useawaitable);
            co_await async_write(socket,boost::asio::buffer(data,n),use_awaitable);
        }
    }catch(std::exception&e){
        std::cout << "Echo Exception Is :" << e.what() << std::endl;
    }
}

我们并不需要知道这里的用法细节,我们主要看

std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data),useawaitable);
co_await async_write(socket,boost::asio::buffer(data,n),use_awaitable);

这段代码,我们通过async_read_some来异步的读取数据,与之前的不同的是,我们没有写回调函数,而是在前面加了一个co_await,并直接用返回值接受.

首先参数的useawaitable的使用,用来标注使用协程.其次co_await的作用是,当遇到异步函数的时候,我们就将协程(简单理解成为当前的函数)挂起(暂停似乎不太对,也差不多).当asio底层异步任务完成之后,协程就又恢复执行,返回值给到了n.

我们可以发现,这种把之前回调的方式,写成了同步的方式.之前的回调方式,遇到异步任务直接返回,底层完成之后,通知到来自动执行回调.这种回调多的时候,很容易迷失自己,尤其是多重回调,这就是回调地狱.这里举个对比例子:

// ✅ 协程:线性逻辑
awaitable<void> process_client() {
    auto header = co_await read_header();
    auto body = co_await read_body(header.length);
    auto result = co_await process_data(header, body);
    co_await send_response(result);
}

// ❌ 回调:嵌套地狱
void process_client() {
    read_header([](Header header) {
        read_body(header.length, [](Body body) {
            process_data(header, body, [](Result result) {
                send_response(result, []() {
                    // 更多嵌套...
                });
            });
        });
    });
}

而这里的写法呢,看上去在co_await async_read_some这里阻塞了,实际上并非阻塞,而是挂起.区别在于,阻塞的话,cpu时间片仍被分配,等待执行.而挂起的话,时间片不再分配给协程,而是去执行任务,之后再恢复执行.

实际上呢,这里的协程底层实现的方式还是回调,但是正如我们上文多次提到的,协程很重要的优势是将异步的代码写作了同步的方式,更加容易理解和判断.至于底层怎么实现了,其实就是异步回调.

简易代码

服务器

#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <iostream>

boost::asio::awaitable<void> echo(boost::asio::ip::tcp::socket sock)
{
    try {
        char data[1024];
        for (;;) {
            std::size_t n = co_await sock.async_read_some(boost::asio::buffer(data), boost::asio::use_awaitable);
            co_await boost::asio::async_write(sock, boost::asio::buffer(data, n), boost::asio::use_awaitable);
        }
    } catch (std::exception& e) {
        std::cerr << "Exception in echo: " << e.what() << std::endl;
    }
}

boost::asio::awaitable<void> listener()
{
    auto executor = co_await boost::asio::this_coro::executor;

    boost::asio::ip::tcp::acceptor acceptor(executor, { boost::asio::ip::tcp::v4(), 9999 });
    for (;;) {
        boost::asio::ip::tcp::socket sock = co_await acceptor.async_accept(boost::asio::use_awaitable);
        co_spawn(executor, echo(std::move(sock)), boost::asio::detached);
    }
}

int main()
{
    try {
        boost::asio::io_context io_context(1);
        boost::asio::co_spawn(io_context, listener(), boost::asio::detached);
        io_context.run();
    } catch (std::exception& e) {
        std::cerr << "Exception in main: " << e.what() << std::endl;
    }
}

客户端

#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <iostream>
#include <string>

int main(){

    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 9999);
    boost::asio::ip::tcp::socket socket(ioc);
    boost::system::error_code error;
    error = socket.connect(endpoint,error);
    if(error){
        std::cout << "Error : " << error.message() << std::endl;
        return 1;
    }

    char data[1024];
    std::cout << "Please enter a message : " ;
    std::cin.getline(data,1024);
    size_t length = std::strlen(data);
    boost::asio::write(socket,boost::asio::buffer(data,length),error);
    if(error){
        std::cout << "Error : " << error.message() << std::endl;
        return 1;
    }
    std::cout << "Message sent successfully." << std::endl;
    char reply[1024];
    size_t reply_length = boost::asio::read(socket,boost::asio::buffer(reply,length),error);
    if(error){
        std::cout << "Error : " << error.message() << std::endl;
        return 1;
    }
    std::cout << "Reply from server : " << std::string(reply,reply_length) << std::endl;

}
posted @ 2025-12-24 23:13  大胖熊哈  阅读(28)  评论(0)    收藏  举报