【问题标题】:Is there a way to explicitly destroy all handlers pending on a given boost::asio::io_context?有没有办法显式销毁所有在给定 boost::asio::io_context 上挂起的处理程序?
【发布时间】:2018-09-03 09:13:53
【问题描述】:

据我所知,据我检查 boost::asio 文档和源代码,除了销毁上下文本身之外,没有办法显式销毁给定 io_context 上的所有待处理处理程序?

如果可能,我需要能够停止 io_context,销毁 io_context 上的挂起处理程序,然后做一些其他事情,最后销毁与给定 io_context 和io_context 本身。

我知道我可以使用 work_guard::reset 并让所有挂起的处理程序运行,然后 io_context 将自行停止,但问题是许多处理程序可能会产生(发布/延迟/等)新的挂起处理程序等,即每个这样的处理程序都需要用“如果停止”之类的东西来保护。

我认为 io_context::shutdown 正是这样做的,但除了继承之外,没有办法显式调用关闭函数,因为它不是公共的。

谢谢。

【问题讨论】:

  • 想到的最简单的方法是重新创建io_context的新实例
  • @sehe 如果我理解正确,您建议对 io_context 进行某种显式破坏(unique_ptr::reset 等)?我考虑过这一点,但有一些依赖于 io_context 的 io_objects(主要是定时器),在这种情况下,它们也需要在 io_context 之前重置,这会使事情变得有点复杂。

标签: c++ boost-asio


【解决方案1】:

使用受保护的shutdown 尝试您的建议会导致我的系统出现段错误。我认为它受到保护是有原因的:)

无论如何,看起来restart/stop/reset 的明智组合可能会完成这项工作。奇怪的是,一些处理程序队列显然停留在周围,除非有人执行(空)run/run_one。事实上,即使是poll_one 似乎也足够了。所以,无论如何,包括那个。

这是我的测试床代码,你可能会发现它很有用:

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;

struct Handler {
    void operator()(boost::system::error_code ec) { std::cout << "Handler invoked: " << ec.message() << std::endl; }

    struct Instance { // logging only unique instance to avoid noise of moved handlers
        Instance()  { std::cout << "Created handler instance"   << std::endl; }
        ~Instance() { std::cout << "Destroyed handler instance" << std::endl; }
    };
    std::unique_ptr<Instance> _instance = std::make_unique<Instance>();
};

int main()
{
    struct Hack : boost::asio::io_context { 
        using boost::asio::io_context::shutdown;
    } io;
    auto work = make_work_guard(io);

    std::cout << " -- run" << std::endl;
    auto t = std::thread([&]{ io.run(); });

    {
        boost::asio::high_resolution_timer tim(io, 2s);
        tim.async_wait(Handler{});
        work.reset(); // no longer needed

        std::this_thread::sleep_for(500ms);

#if 1
        io.stop();
#else
        io.shutdown(); // segfaults
#endif
    }

    std::cout << " -- timer destructed" << std::endl;
    std::cout << " -- joining" << std::endl;
    t.join();

    std::cout << " -- empy run to flush handler queue" << std::endl;
    io.reset();
    //io.run();
    //io.run_one();
    io.poll_one();

    std::cout << " -- bye" << std::endl;
}

打印

 -- run
Created handler instance
 -- timer destructed
 -- joining
 -- empy run to flush handler queue
Handler invoked: Operation canceled
Destroyed handler instance
 -- bye

更新

这是我最好的建议(我猜除了not sharing io):

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;

struct Handler {
    void operator()(boost::system::error_code ec) { std::cout << "Handler invoked: " << ec.message() << std::endl; }

    struct Instance { // logging only unique instance to avoid noise of moved handlers
        Instance()  { std::cout << "Created handler instance"   << std::endl; }
        ~Instance() { std::cout << "Destroyed handler instance" << std::endl; }
    };
    std::unique_ptr<Instance> _instance = std::make_unique<Instance>();
};

int main()
{
    std::unique_ptr<boost::asio::io_context> io;

    int i = 1;
    for (auto delay : { 1500ms, 500ms }) {
        std::cout << " ------------------- reinitialized -------------- \n";
        io = std::make_unique<boost::asio::io_context>();
        boost::asio::high_resolution_timer tim(*io, 1s);

        std::cout << i << " -- run" << std::endl;
        auto t = std::thread([&]{ io->run(); });

        tim.async_wait(Handler{});

        std::this_thread::sleep_for(delay);

        std::cout << i << " -- stop" << std::endl;
        io->stop();

        std::cout << i << " -- joining" << std::endl;
        t.join();

        std::cout << " ------------------- destruct ------------------- \n";
        io.reset();
    }

    std::cout << "Bye" << std::endl;
}

打印

 ------------------- reinitialized -------------- 
1 -- run
Created handler instance
Handler invoked: Success
Destroyed handler instance
1 -- stop
1 -- joining
 ------------------- destruct ------------------- 
 ------------------- reinitialized -------------- 
1 -- run
Created handler instance
1 -- stop
1 -- joining
 ------------------- destruct ------------------- 
Destroyed handler instance
Bye

【讨论】:

  • 我也觉得这种行为有点令人惊讶。就我测试这样的解决方案而言,仅使用 post-ed 处理程序,即使在重置之后,处理程序仍保留在 io_context 队列中,当我调用 io.run 时,它们再次被执行。问题是发布的处理程序无法知道(除非使用显式标志)上下文是否已停止并重置。
  • 是的。老实说,我会这样做:coliru.stacked-crooked.com/a/686a3a5914333f71(或者甚至不分享coliru.stacked-crooked.com/a/043e8b63ee98e52e
  • 看来这是目前io_context接口能做到的最好的了
  • 谢谢!我已经准备好了,不能总是停止服务。通过在resetjoin 之间添加poll_one,它不再挂起。魔鬼在细节中。
  • @PavelVazharov 我不知道,如果这种行为如此出乎意料:stop() 信号 run() 和朋友不要再处理任何待处理的工作并返回。 reset() 只是重置了那个停止位,也许它的名字有点不清楚。因此,只要您需要一个干净的io_context,只需构建一个新的@。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-19
  • 2019-05-02
  • 2022-12-23
相关资源
最近更新 更多