0

对于嵌入式系统接口,我正在实现一个具有多达 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 新手)

谢谢你,妈妈。

4

1 回答 1

0

这是预期的行为。

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中可以找到一种方法。
于 2013-11-01T17:02:23.837 回答