boost::asio 调用了错误的串行端口处理程序

boost::asio calls the wrong handler for serial port

本文关键字:串行端口 处理 程序 asio 调用 boost 错误      更新时间:2023-10-16

对于嵌入式系统接口,我正在实现一个具有多达 8 种读取模式的类(下面的代码仅说明了两种 - FREERUN 和 BLOCKRUN)用于读取串行端口。此读取器的简化 ReaderTest() 函数如下所示:

void CSerialBoost::ReaderTest()
{
while (RUNflag)
    switch (RUNstate) {
    case FREERUN:
            port.async_read_some(asio::buffer(TextIn, SZTXT),
                boost::bind(&CSerialBoost::OnReadFree, this, asio::placeholders::error, asio::placeholders::bytes_transferred));
            server.reset();
            server.run(error);      // it calls OnReadBlock() here
            break;
    case BLOCKRUN:
            port.async_read_some(asio::buffer(TextIn, SZTXT),
                boost::bind(&CSerialBoost::OnReadBlock, this, asio::placeholders::error, asio::placeholders::bytes_transferred));
            server.reset();
            server.run(error);       // it calls OnReadFree() here
            break;
    default:    break;
    }
    port.cancel(error); // cancel all IO operations
}

类 CSerialBoost 具有以下成员:

asio::io_service    server;
asio::serial_port   port;
asio::error_code    error;
volatile int    RUNstate;   // reader mode
volatile int    RUNflag;    // start/stop flag

当我从一种模式切换到另一种模式时出现意外行为。假设没有传入数据并且代码在 FREERUN 中运行,为了从另一个线程切换到 BLOCKRUN,我这样做:

RUNstate = BLOCKRUN;
server.stop();      // unblock the event loop

该操作切换到 BLOCKRUN,因为它应该,当它到达 BLOCKRUN 案例下的 server.run(error) 行时,它会调用 CSerialBoost::OnReadFree() 函数,错误operation_aborted。当它切换回 FREERUN 时也会发生同样的情况 - 当它在 FREERUN 案例下到达 server.run(error) 时,它会调用 CSerialBoost::OnReadBlock()。

这是非常具有误导性的,因为它总是调用其他模式的函数。当我停止/取消 IO 服务时,我希望每个案例都调用自己的函数(或不调用)。是我期望过高,还是这是正常操作?我做错了什么吗?请提示我如何处理这个问题。(我在 Win XP 和 Win 7、Visual Studio 2010 下使用 boost:asio 1-5-3,我是 boost 的新手)

谢谢你,马。

这是

预期的行为。

io_service::stop()io_service::reset()只控制io_service的事件循环的状态;都不会影响计划用于延迟调用(准备运行)的处理程序或用户定义的处理程序对象的生命周期。

在调用server.run()时,async_read_some操作已排队进入io_service。 当事件循环通过 server.stop() 显式停止时,如果尚未调用操作的完成处理程序,则操作或完成处理程序将保持排队io_service。 然后,执行在while循环中继续,port.cancel()强制取消port上的未完成操作,将其完成处理程序设置为准备运行,错误代码为 boost::asio::error::operation_aborted 。 因此,下次调用server.run()时,将执行已取消操作的准备运行的处理程序。

请考虑以下任一情况:

  • 运行io_service完成。 这通常需要设置状态、取消未完成的操作以及防止完成处理程序将其他工作发布到io_service中。 Boost.Asio 提供了一个官方的超时示例,此处还显示了从io_service运行到完成的超时方法。
  • 让异步调用链实现状态机。 这将避免stop()reset()和重新run() io_service的需要。
  • 控制io_service对象的生命周期,因为io_service的析构函数将导致所有未完成的处理程序被销毁 在此答案中可以找到一种方法。