boost::asio::io_context的事件循环

转自: https://www.jianshu.com/p/d6ae8adb5914

简介
boost::asio::io_context看字面意思是io的上下文, 可以理解为, boost的任何io都会涉及到一个io_context, 同步io会隐式地启动一个io_context, 而异步io需要我们指定一个, 然后在合适的时机调用io_context::run()函数, 之后会进入io事件循环, 如果io完成会调用回调函数, 保证实现有关的功能. 根据官方文档, 在已经启动io_context::run()的情况下, 如果此时没有了io操作, 那么io_context会自动取消事件循环, 那么此时如果再有异步io回调, 也不会发生作用了. 参考下面的代码:

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

std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6768;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
    boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock1(ioc, ep.protocol());

    sock.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });
    ioc.run();

    // 这个函数也会失败, 但是此时不会调用回调函数了, 因为run的事件循环已经结束了
    sock1.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    return 0;
}
/*
只会输出一个Connection refused消息, sock1的回调函数不会被调用
*/

但是有些情况下, 我们希望run()函数的事件循环在没有io事件的情况下, 也不会退出事件循环, 而是一直等待, 当有了新的异步io调用的时候, 还可以继续使用该循环.

放在上述的代码中, 意思就是sock1的异步连接的回调函数还可以使用

具体实现
需要借助boost::asio::io_context::work来实现, 这个类的核心作用就是防止io_context在没有io事件的情况退出。
直接使用:

boost::asio::io_context::work(boost::asio::io_context & io_context);

的方式进行构造, 当work对象被销毁时, 它的作用就自动停止了.下面给出代码说明.
代码示例:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <chrono>

std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6768;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint
    ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
    boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock1(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock2(ioc, ep.protocol());

    boost::asio::io_context::work worker(ioc);
    std::thread t([&ioc](){ioc.run();});

    std::cout << "Main thread will for 1 seconds...\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 这里是防止回调启动过快
    std::cout << "Main thread weak up...\n";

    sock.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    sock1.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    sock2.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    std::cout << "Main thread will for 1 seconds...\n";  // 这里是为了防止stop()执行过快
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Main thread weak up...\n";
    ioc.stop();  // 先显式停止io_context, 否则无法终止
    t.join();
    return 0;
}

正常输出:

Main thread will for 1 seconds...
Main thread weak up...
Main thread will for 1 seconds...
Connection refused
Connection refused
Connection refused
Main thread weak up...

如果注释掉boost::asio::io_context::work worker(ioc);这一句, 那么会输出:

Main thread will for 1 seconds...
Main thread weak up...
Main thread will for 1 seconds...
Main thread weak up...

也就是说, 事件循环在没有IO事件的情况下, 就自动结束了.

总结
在不确定io异步事件回调发生的情况下, 如果想要io_context事件循环一直进行, 可以使用boost::asio::io_context::work worker(ioc);来执行, 需要开启一个新的线程, 专门执行IO事件即可, 具体参考上述代码.

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`asio::io_context::strand::post` 和 `asio::steady_timer::expires_after` 都是 Boost.Asio 库中的功能。 `asio::io_context::strand::post` 可以将一个函数或者 Lambda 表达式提交到指定的 `strand` 上执行。`strand` 是 Boost.Asio 中用于处理多个线程访问同一个 `io_context` 的线程安全机制。 例如: ```cpp asio::io_context io_context; asio::io_context::strand strand(io_context); void handler1() { std::cout << "handler1: " << std::this_thread::get_id() << std::endl; } void handler2() { std::cout << "handler2: " << std::this_thread::get_id() << std::endl; } void post_handlers() { strand.post(handler1); strand.post(handler2); } std::thread t1(post_handlers); std::thread t2(post_handlers); io_context.run(); t1.join(); t2.join(); ``` 在上面的代码中,我们创建了一个 `io_context` 和一个 `strand`。然后我们创建了两个函数 `handler1` 和 `handler2`,并将它们都提交到了 `strand` 上执行。接下来,我们创建了两个线程分别执行 `post_handlers` 函数,这个函数会将 `handler1` 和 `handler2` 提交到 `strand` 上。最后我们启动了 `io_context` 的事件循环并等待两个线程执行完毕。 `asio::steady_timer::expires_after` 是一个定时器的功能,可以设置定时器在一定时间后超时。例如: ```cpp asio::io_context io_context; asio::steady_timer timer(io_context); void handler(const boost::system::error_code& error) { if (!error) { std::cout << "timer expired" << std::endl; } } void start_timer() { timer.expires_after(std::chrono::seconds(1)); timer.async_wait(handler); } start_timer(); io_context.run(); ``` 在上面的代码中,我们创建了一个 `io_context` 和一个 `steady_timer`。然后我们定义了一个回调函数 `handler`,当定时器超时时会被调用。接下来,我们创建了一个函数 `start_timer`,它会设置定时器超时时间为 1 秒,并异步等待定时器超时。最后我们启动了 `io_context` 的事件循环,当定时器超时时,`handler` 函数会被调用。 需要注意的是,`asio::steady_timer::expires_after` 的时间是相对时间,而不是绝对时间。如果需要设置绝对时间,可以使用 `asio::steady_timer::expires_at`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值