• Boost ASIO:io_service 与 strand 的使用


    Scalability and Multithreading

    Abstract

    Developing a program based on a library like Boost.Asio differs from the usual C++ style. Functions that may take longer to return are no longer called in a sequential manner. Instead of calling blocking functions, Boost.Asio starts asynchronous operations. Functions which should be called after an operation has finished are now called within the corresponding handler. The drawback of this approach is the physical separation of sequentially executed functions, which can make code more difficult to understand.

    A library such as Boost.Asio is typically used to achieve greater efficiency. With no need to wait for an operation to finish, a program can perform other tasks in between. Therefore, it is possible to start several asynchronous operations that are all executed concurrently – remember that asynchronous operations are usually used to access resources outside of a process. Since these resources can be different devices, they can work independently and execute operations concurrently.

    Scalability describes the ability of a program to effectively benefit from additional resources. With Boost.Asio it is possible to benefit from the ability of external devices to execute operations concurrently. If threads are used, several functions can be executed concurrently on available CPU cores. Boost.Asio with threads improves the scalability because your program can take advantage of internal and external devices that can execute operations independently or in cooperation with each other.

    如果在boost::asio::io_service类型的对象上调用成员函数run(),那么关联的处理程序将在同一个线程中调用。通过使用多线程,程序可以多次调用run()。一旦异步操作完成,I/O服务对象将在其中一个线程中执行处理程序。如果第二个操作在第一个操作之后不久完成,那么I/O服务对象可以在另一个线程中执行处理程序。现在,不仅进程外的操作可以并发执行,进程内的处理程序也可以并发执行。

    Demo

    #include 
    #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{3}};
      timer2.async_wait([](const boost::system::error_code &ec)
        { std::cout << "3 sec\n"; });
    
      std::thread thread1{[&ioservice](){ ioservice.run(); }};
      std::thread thread2{[&ioservice](){ ioservice.run(); }};
      thread1.join();
      thread2.join();
    }

    前面的示例已在示例32.3中转换为多线程程序。使用std::thread,在main()中创建两个线程。run()在每个线程中唯一的I/O服务对象上调用。这使得当异步操作完成时,I/O服务对象可以使用两个线程来执行处理程序。

    在例32.3中,两个闹钟都应该在三秒后响起。因为有两个线程可用,所以两个lambda函数都可以并发执行。如果第一个闹钟的处理程序正在执行时第二个闹钟响了,那么这个处理程序可以在第二个线程中执行。如果第一个闹钟的处理程序已经返回,那么I/O服务对象可以使用任何线程来执行第二个处理程序。

    当然,使用线程并不总是有意义的。例32.3可能不会按顺序将消息写入标准输出流。相反,它们可能被混淆了。两个处理程序可以同时在两个线程中运行,它们共享全局资源std::cout。为了避免中断,对std::cout的访问需要同步。如果处理程序不能并发执行,那么线程的优势就会丧失。

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

    在单个I/O服务对象上反复调用run()是编写基于Boost的程序的推荐方法。Asio更具有可伸缩性。但是,您可以创建多个I/O服务对象,而不是为一个I/O服务对象提供多个线程。

    在例32.4中,在boost::asio::steady_timer类型的两个闹钟旁边使用两个I/O服务对象。该程序基于两个线程,每个线程绑定到另一个I/O服务对象。两个I/O对象timer1和timer2不再绑定同一个I/O服务对象。它们被绑定到不同的对象。

    例32.4的工作方式与以前相同。不可能给出关于何时使用多个I/O服务对象有意义的一般性建议。因为boost::asio::io_service代表一个操作系统接口,任何决策都取决于特定的接口。

    在Windows上,boost::asio::io_service通常基于IOCP,在Linux上,它基于epoll()。拥有多个I/O服务对象意味着将使用多个I/O完成端口,或者将多次调用epoll()。这是否比只使用一个I/O完成端口或调用epoll()更好,取决于具体情况。

    Other

    除此之外,还有一个基于io_service 的类,名字叫strand, 最大的区别就是strand 是同步执行被管理的操作,顺序就是注册操作的顺序。

  • 相关阅读:
    vite.config.js-element-plus
    索引(1)
    008:安装Docker
    一台TrinityCore服务器客户端连接网速慢(未解决)
    【React】React-Redux基本使用
    微信小程序组件传值
    SSM框架学习之spring
    从抓包砍到接口测试,五分钟看完全过程解析,还说你不会测试?
    Python基础095:Python读取PDF中的字符
    torch车牌字符识别-数据集搭建(四)
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/126545830