您的位置:

探究boost::asio::io_service

一、基本介绍

boost::asio::io_service是boost库中实现异步编程的关键组件,它提供了一个事件循环机制,负责处理I/O操作、定时器、信号等事件,同时其多线程支持,可以在多个线程中并行执行任务,增强了程序的并发性。

boost::asio::io_service定义于asio库的头文件中,创建一个io_service对象通常使用默认的构造函数,没有任何参数,如下所示:

boost::asio::io_service io_service;

当有I/O操作(如异步的读写socket)需要进行时,我们需要将该操作添加到io_service的队列中,同时运行io_service,等待事件的发生,代码如下:

io_service.run();

run()方法将会等待io_service中任何派生自boost::asio::io_service::work的对象被引用的引用计数变为0才会退出,也就是说,如果你希望asio库一直工作,就要维护至少一个work对象。

二、工作模式

为了提高程序的性能、并发性,asio库提供了许多多线程支持的类,例如io_service::work,io_service::strand。针对io_service对象,有两种工作模式:

1、单线程模式

在单线程模式下,所有I/O事件都在一个线程中被处理,由于没有线程切换的开销,原生的单线程模式的性能是最好的。一般情况下,我们通常在主线程中创建io_service对象,并且没有任何其他的线程参与,代码如下:

boost::asio::io_service io_service;
//添加恒定的work对象,保证io_service一直工作
boost::asio::io_service::work work(io_service); 
io_service.run();

在这个例子中,我们在io_service的队列中永远存在一个work,所以io_service.run()会一直运行,直到该work被销毁。

2、多线程模式

当有多个线程需要共享I/O操作,如异步socket读写时,我们可以在多个线程中同时运行io_service,代码如下:

boost::asio::io_service io_service;
//添加恒定的work对象,保证io_service一直工作
boost::asio::io_service::work work(io_service);
//创建多个线程执行io_service的run方法
std::vector threads; 
for (std::size_t i = 0; i < num_threads; ++i)
{
    threads.emplace_back([&io_service]() {
        io_service.run();
    });
}
//等待所有线程执行完run方法后再退出
for (auto& t : threads)
{
    t.join();
}

  

在上述代码中,我们先创建了一个io_service对象,并始终保持一个io_service::work对象,然后创建若干线程,每个线程都调用io_service的run()方法以处理I/O事件。最后,等待所有的线程完成run()方法后再退出。

三、io_service::strand(协程)

io_service::strand是另外一个很重要的类,它实现了线程安全的FIFO队列,保证了在同一个strand对象中提交的所有handler被按照提交的先后顺序依次执行(但保证不了跨strand对象的handler之间的顺序)。这种技术和协程类似,又称为“异步协程”。将io_service::strand应用于handler处理程序可以消除并发数据访问所产生的数据竞争和死锁,并且可以保证每个handler的独立执行。

我们通过boost::asio::io_service::strand::wrap()创建一个新的handler,在strand对象中执行,如下代码所示:

boost::asio::io_service::strand strand(io_service);
boost::asio::async_read(socket, buffer,
    strand.wrap([](const boost::system::error_code& ec,
                   std::size_t bytes_transferred) {
        //read完成的回调函数
    })
);

在上述代码中,我们使用async_read方法读取socket的数据,并且将回调函数封装到strand执行,这样就保证了回调的线程安全。

四、定时器(timer)

Asio库中提供了定时器处理机制,这类工作放到io_service的队列中,由io_service在指定时间触发指定handler。代码如下所示:

boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(5));
timer.async_wait([&](const boost::system::error_code& error){
    if (!error) {
        //定时器超时后执行的操作
    }
});

在上述代码中,我们创建了一个以秒为单位的定时器timer,并在该定时器超时指定时间后进行回调操作。

五、信号(signal)

Asio库还提供了闹钟信号(signal)处理,使用方法与定时器类似,但是信号仅仅对UNIX系统起作用,Windows系统不支持。代码如下所示:

boost::asio::signal_set signals(io_service, SIGINT, SIGTERM);
signals.async_wait([=](const boost::system::error_code& error,
                       int signal_number) {
    //处理信号
    if (!error) {
        io_service.stop();
    }
});

在上述代码中,我们创建了一个信号集合,关注的信号包括SIGINT和SIGTERM,当这两个信号被触发时,程序会执行相关回调函数中的代码。

六、总结

通过对boost::asio::io_service的多个方面的探究,我们了解了该组件实现异步编程的关键性,同时掌握了其多线程支持、协程、定时器、信号等特点。对于实现高效异步编程的应用程序,boost::asio::io_service是一个必须要了解和掌握的工具。