• Boost ASIO :I/O Services and I/O Objects


    I/O Services and I/O Objects

    Abstract

    Programs that use Boost.Asio for asynchronous data processing are based on I/O services and I/O objects. I/O services abstract the operating system interfaces that process data asynchronously. I/O objects initiate asynchronous operations. These two concepts are required to separate tasks cleanly: I/O services look towards the operating system API, and I/O objects look towards tasks developers need to do.

    使用Boost的程序。异步数据处理的Asio是基于I/O服务和I/O对象的。

    I/O服务本质上是抽象了异步处理数据的操作系统接口。

    I/O对象则是发起的异步操作。

    这两个概念用于清晰地分离任务与调度器:

    I/O服务着眼于操作系统API可以理解为上下文调度管理,而I/O对象着眼于开发人员需要完成的任务。

    How

    As a user of Boost.Asio you normally don’t connect with I/O services directly. I/O services are managed by an I/O service object. An I/O service object is like a registry where I/O services are listed. Every I/O object knows its I/O service and gets access to its I/O service through the I/O service object.

    在使用ASIO I/O service 的时候,不需要直接去连接操作service,I/Oservice 就像是一个注册表一样,可以将我们的一些异步操作注册在里面,每个I/Oservice 对象独立管理自己的操作。

    Boost.Asio defines boost::asio::io_service, a single class for an I/O service object. Every program based on Boost.Asio uses an object of type boost::asio::io_service. This can also be a global variable.

    While there is only one class for an I/O service object, several classes for I/O objects exist. Because I/O objects are task oriented, the class that needs to be instantiated depends on the task. For example, if data has to be sent or received over a TCP/IP connection, an I/O object of type boost::asio::ip::tcp::socket can be used. If data has to be transmitted asynchronously over a serial port, boost::asio::serial_port can be instantiated. If you want to wait for a time period to expire, you can use the I/O object boost::asio::steady_timer.

    boost::asio::steady_timer is like an alarm clock. Instead of waiting for a blocking function to return when the alarm clock rings, your program will be notified. Because boost::asio::steady_timer just waits for a time period to expire, it would seem as though no external resource is accessed. However, in this case the external resource is the capability of the operating system to notify a program when a time period expires. This frees a program from creating a new thread just to call a blocking function. Since boost::asio::steady_timer is a very simple I/O object, it will be used to introduce Boost.Asio.

    简单的说,这里面有几个核心的api、类,包括了io_service(IO管理器), ip::tcp::socket(socket连接器),串口(serial_port),定时器(steady_time)

    Demo

    #include 
    #include 
    #include 
    #include 
    
    using namespace boost::asio;
    
    int main()
    {
      io_service ioservice;
    
      steady_timer timer{ioservice, std::chrono::seconds{3}};
      timer.async_wait([](const boost::system::error_code &ec)
        { std::cout << "3 sec\n"; });
    
      ioservice.run();
    }

    Example 32.1 creates an I/O service object, ioservice, and uses it to initialize the I/O object timer. Like boost::asio::steady_timer, all I/O objects expect an I/O service object as a first parameter in their constructor. Since timer represents an alarm clock, a second parameter can be passed to the constructor of boost::asio::steady_timer that defines the specific time or time period when the alarm clock should ring. In Example 32.1, the alarm clock is set to ring after 3 seconds. The time starts with the definition of timer.

    Instead of calling a blocking function that will return when the alarm clock rings, Boost.Asio lets you start an asynchronous operation. To do this, call the member function async_wait(), which expects a handler as the sole parameter. A handler is a function or function object that is called when the asynchronous operation ends. In Example 32.1, a lambda function is passed as a handler.

    async_wait() returns immediately. Instead of waiting three seconds until the alarm clock rings, the lambda function is called after three seconds. When async_wait() returns, a program can do something else.

    A member function like async_wait() is called non-blocking. I/O objects usually also provide blocking member functions as alternatives. For example, you can call the blocking member function wait() on boost::asio::steady_timer. Because this member function is blocking, no handler is passed. wait() returns at a specific time or after a time period.

    上述的代码,本质上就是一个协程的典型应用,async_wait()会立即返回。不是等待三秒直到闹钟响,而是在三秒后调用lambda函数。当async_wait()返回时,程序可以执行其他操作。

    像async_wait()这样的成员函数被称为非阻塞函数。I/O对象通常也提供阻塞成员函数作为替代。例如,你可以在boost::asio::steady_timer上调用阻塞成员函数wait()。因为这个成员函数是阻塞的,所以没有传递处理程序。Wait()在特定时间或一段时间后返回。

    #include 
    #include 
    #include 
    #include 
    
    using namespace boost::asio;
    
    int main()
    {
      io_service ioservice;
    
      steady_timer timer1{ioservice, std::chrono::seconds{3}};
      timer1.async_wait([](const boost::system::error_code &ec)
        { std::cout << "3 sec\n"; });
    
      steady_timer timer2{ioservice, std::chrono::seconds{4}};
      timer2.async_wait([](const boost::system::error_code &ec)
        { std::cout << "4 sec\n"; });
    
      ioservice.run();
    }

    In Example 32.2, two objects of type boost::asio::steady_timer are used. The first I/O object is an alarm clock that rings after three seconds. The other is an alarm clock ringing after four seconds. After both time periods expire, the lambda functions that were passed to async_wait() will be called.

    run() is called on the only I/O service object in this example. This call passes control to the operating system functions that execute asynchronous operations. With their help, the first lambda function is called after three seconds and the second lambda function after four seconds.

    It might come as a surprise that asynchronous operations require a call to a blocking function. However, this is not a problem because the program has to be prevented from exiting. If run() wouldn’t block, main() would return, and the program would exit. If you don’t want to wait for run() to return, you only need to call run() in a new thread.

    The reason why the examples above exit after a while is that run() returns if there are no pending asynchronous operations. Once all alarm clocks have rung, no asynchronous operations exist that the program needs to wait for.

    ioservice.run()会导致程序阻塞直到所有的异步操作全部执行完毕。

  • 相关阅读:
    驱动开发:内核R3与R0内存映射拷贝
    网络协议
    Prometheus(一)——概述、监控体系、生态组件、部署
    Mysql
    flink增量检查点启动恢复的时间是很久的,业务上不能接受,怎么处理
    【强化学习论文合集】ICRA-2022 强化学习论文 | 2022年合集(六)
    Django系列15-员工管理系统实战--订单管理
    vue设置全局变量:让你的数据无处不在!
    2022.9.1 SAP RFC
    Ubuntu-22.04通过RDP协议连接远程桌面
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/126527672