boost::asio::io_ Event loop of context

Posted by alexks on Thu, 20 Jan 2022 20:49:52 +0100

Transferred from: https://www.jianshu.com/p/d6ae8adb5914

brief introduction
boost::asio::io_context literally means the context of Io. It can be understood that any io of boost will involve an io_context, synchronizing IO will implicitly start an io_context, and asynchronous IO requires us to specify one, and then call IO at the right time_ The context:: run() function will then enter the IO event loop. If IO is completed, the callback function will be called to ensure the implementation of relevant functions According to the official documentation, the IO has been started_ In the case of context:: run(), if there is no IO operation at this time, then io_context will automatically cancel the event loop, so if there is another asynchronous IO callback at this time, it will not work Refer to the following code:

#include <iostream>
#include <boost/asio.hpp>

std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6768;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
    boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock1(ioc, ep.protocol());

    sock.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });
    ioc.run();

    // This function will also fail, but the callback function will not be called at this time, because the event loop of run has ended
    sock1.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    return 0;
}
/*
Only one connection rejected message will be output, and the callback function of sock1 will not be called
*/

However, in some cases, we hope that the event loop of the run() function will not exit the event loop without io events, but wait all the time. When there are new asynchronous io calls, we can continue to use the loop

In the above code, it means that the callback function of asynchronous connection of sock1 can also be used

Concrete implementation
You need to use boost::asio::io_context::work. The core function of this class is to prevent io_context exits without IO event.
Direct use:

boost::asio::io_context::work(boost::asio::io_context & io_context);

When the work object is destroyed, its function will stop automatically The code description is given below
Code example:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <chrono>

std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6768;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint
    ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
    boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock1(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock2(ioc, ep.protocol());

    boost::asio::io_context::work worker(ioc);
    std::thread t([&ioc](){ioc.run();});

    std::cout << "Main thread will for 1 seconds...\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));  // This is to prevent the callback from starting too fast
    std::cout << "Main thread weak up...\n";

    sock.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    sock1.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    sock2.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    std::cout << "Main thread will for 1 seconds...\n";  // This is to prevent stop() from executing too fast
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Main thread weak up...\n";
    ioc.stop();  // Explicitly stop IO first_ Context, otherwise it cannot be terminated
    t.join();
    return 0;
}

Normal output:

Main thread will for 1 seconds...
Main thread weak up...
Main thread will for 1 seconds...
Connection refused
Connection refused
Connection refused
Main thread weak up...

If boost:: ASIO:: IO is commented out_ context::work worker(ioc); This sentence will output:

Main thread will for 1 seconds...
Main thread weak up...
Main thread will for 1 seconds...
Main thread weak up...

In other words, the event loop ends automatically without IO events

summary
When you are not sure that the IO asynchronous event callback occurs, if you want IO_ The context event loop keeps going. You can use boost::asio::io_context::work worker(ioc); To execute, you need to start a new thread to execute IO events. For details, refer to the above code

Topics: C++ Linux