传递给boost :: asio :: async_read_some的回调从未在使用中调用,其中boost :: asio :: read_some返回数据

时间:2014-09-27 03:34:37

标签: boost serial-port boost-asio

我一直致力于通过使用boost :: asio :: basic_serial_port学习基本的串行终端示例来实现半双工串行驱动程序: http://lists.boost.org/boost-users/att-41140/minicom.cpp

我需要异步读取但仍然检测处理程序何时在主线程中完成,因此我使用boost:bind在asda_read_some中传递一个带有几个额外引用参数的回调函数。永远不会调用处理程序但是如果我用read_some函数替换async_read_some函数,它会返回没有问题的数据。

我相信我满足了这个函数调用处理程序的所有必要条件,因为它们与asio :: read一样,返回一些函数:

  1. 缓冲区留在范围
  2. 串行设备接收一个或多个字节
  3. io服务正在运行
  4. 端口已打开并以正确的波特率运行
  5. 有没有人知道我是否缺少异步读取特有的另一个假设,或者我是不是正确设置了io_service?

    以下是我如何使用async_read_some(http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/basic_serial_port/async_read_some.html)代码的示例:

    void readCallback(const boost::system::error_code& error, size_t bytes_transfered, bool & finished_reading, boost::system::error_code& error_report, size_t & bytes_read)
    {
        std::cout << "READ CALLBACK\n";
        std::cout.flush();
        error_report = error;
        bytes_read = bytes_transfered;
        finished_reading = true;
        return;
    }
    
    int main()
    {
        int baud_rate = 115200;
        std::string port_name = "/dev/ttyUSB0";
        boost::asio::io_service io_service_;
        boost::asio::serial_port serial_port_(io_service_,port_name);
        serial_port_.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
        boost::thread service_thread_;
        service_thread = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_));
    
        std::cout << "Starting byte read\n";
        boost::system::error_code ec;
    
        bool finished_reading = false;
        size_t bytes_read;
        int max_response_size = 8;
        uint8_t read_buffer[max_response_size];
        serial_port_.async_read_some(boost::asio::buffer(read_buffer, max_response_size),
                         boost::bind(readCallback,
                                     boost::asio::placeholders::error,
                                     boost::asio::placeholders::bytes_transferred,
                                     finished_reading, ec, bytes_read));
    
        std::cout << "Waiting for read to finish\n";
        while (!finished_reading)
        {
            boost::this_thread::sleep(boost::posix_time::milliseconds(1));
        }
    
        std::cout << "Finished byte read: " << bytes_read << "\n";
    
        for (int i = 0; i < bytes_read; ++i)
        {
        printf("0x%x ",read_buffer[i]);
        }
    }
    

    结果是回调没有打印出任何内容,而while!finished循环永远不会完成。

    以下是我使用阻塞read_some函数的方法(boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/basic_serial_port/read_some.html):

    int main()
    {
        int baud_rate = 115200;
        std::string port_name = "/dev/ttyUSB0";
        boost::asio::io_service io_service_;
        boost::asio::serial_port serial_port_(io_service_,port_name);
        serial_port_.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
        boost::thread service_thread_;
        service_thread = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_));
    
        std::cout << "Starting byte read\n";
        boost::system::error_code ec;
        int max_response_size = 8;
        uint8_t read_buffer[max_response_size];
        int bytes_read = serial_port_.read_some(boost::asio::buffer(read_buffer, max_response_size),ec);
    
        std::cout << "Finished byte read: " << bytes_read << "\n";
    
        for (int i = 0; i < bytes_read; ++i)
        {
        printf("0x%x ",read_buffer[i]);
        }
    }
    

    这个版本打印我发送的1到8个字符,阻塞直到至少发送一个。

2 个答案:

答案 0 :(得分:1)

请注意boost::bind copies its arguments。如果您想通过引用传递参数,请使用boost::ref(或std::ref)包装它:

boost::bind(readCallback, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, boost::ref(finished_reading), ec, bytes_read));

(但是,严格来说,您传递给另一个帖子的bool变量存在竞争条件。更好的解决方案是使用std::atomic_bool。)

答案 1 :(得分:1)

代码不保证io_service正在运行。 io_service::run()会在以下情况下返回:

  • 所有工作已经完成,没有更多的处理程序可以发送
  • io_service已停止。

在这种情况下,可以在启动serial_port::async_read_some()操作之前创建service_thread_并调用io_service::run(),将工作添加到io_service。因此,service_thread_可以立即从io_service::run()返回。要解决此问题,请:

  • 在启动异步操作后调用io_service::run()
  • 在启动service_thread_之前创建一个io_service::work对象。 work对象会阻止io_service停止工作。

answer可以更深入地了解io_service::run()的行为。


要注意的其他一些事项,并在Igor's answer上展开:

  • 如果一个线程在等待异步操作完成时没有以有意义的方式进行(即在循环休眠中旋转),那么可能值得检查混合同步行为与异步操作是否是正确的解决方案。
  • boost::bind() 按值复制其参数。要通过引用传递参数,请使用boost::ref()boost::cref()

    进行包装
    boost::bind(..., boost::ref(finished_reading), boost::ref(ec),
                boost::ref(bytes_read));
    
  • 需要添加同步以保证主线程中finished_reading的内存可见性。对于异步操作,Boost.Asio将guarantee适当的内存屏障以确保正确的内存可见性(有关详细信息,请参阅此answer)。在这种情况下,主线程中需要memory barrier以保证主线程观察到其他线程对finished_reading的更改。请考虑使用Boost.Thread同步机制(如boost::mutex)或Boost.Atomic的atomic objectsthread and signal fences