上一篇介绍了Boost.Asio的一些特性,但是相对抽象和离散,这篇博客从Boost.Asio的基础知识一步步深入,读完之后对Boost.Asio会有全面的了解和掌握。Boost.Asio通过采用C++以及Boost库的语法特性,提供跨平台的异步网络IO能力。因此使用Boost.Asio需要基本的网络知识,C++、Boost知识。
io_service是Boost.Asio的核心类,run()方法是它的重要方法之一,后续会陆续介绍到其dispatch,post,stop,reset等重要方法。run()方法的作用是阻塞程序直到所有的工作都完成,所有的handler都dispatch了;或者当调用stop方法时,run()方法也会不再阻塞下去。下面是一个说明run()方法的示例程序:
#include <boost/asio.hpp>
#include <iostream>
int main( int argc, char * argv[] )
{
boost::asio::io_service io_service;
io_service.run();
std::cout << "Do you reckon this line displays?" << std::endl;
return 0;
}
在Linux下在安装完Boost后,采用g++ 1a.cpp -o 1a -lboost_system进行编译(Boost.Asio会调用boost_system库的内容,所以需要指定链接内容,涉及到线程和多线程时,则分别要做如下指定 -pthread -lboost_thread-mt)。运行可执行文件,我们发现会有输出,这是因为没有给io_service添加任务,它自然就结束了,不会阻塞。
通常我们不希望程序自动退出,因为可能还有其它任务需要处理,更多的时候希望控制程序何时退出,Boost.Asio考虑到了这点,看下面的例子:
#include <boost/asio.hpp>
#include <iostream>
int main( int argc, char * argv[] )
{
boost::asio::io_service io_service;
boost::asio::io_service::work work( io_service );
io_service.run();
std::cout << "Do you reckon this line displays?" << std::endl;
return 0;
}
运行上面的例子,我们发现程序没有输出也不退出,在按Ctrl+C后程序才会终止。这主要是因为我们给了io_service工作---work,它在没有完成工作的情况时是不会退出的,在io_service.run()从io_service中删除work,程序就退出了。如果不喜欢用阻塞的方法来运行事件处理,Boost.Asio也提供了其它类似BSD socket的任务调度方法,poll就是其中之一。poll调用io_service的事件循环来运行已经就绪的handler方法,下面是一个poll的示例(用for模拟程序运行的循环):
#include <boost/asio.hpp>
#include <iostream>
int main( int argc, char * argv[] )
{
boost::asio::io_service io_service;
for( int x = 0; x < 42; ++x )
{
io_service.poll();
std::cout << "Counter: " << x << std::endl;
}
return 0;
}
#include <boost/asio.hpp>
#include <iostream>
int main( int argc, char * argv[] )
{
boost::asio::io_service io_service;
boost::asio::io_service::work work( io_service );
for( int x = 0; x < 42; ++x )
{
io_service.poll();
std::cout << "Counter: " << x << std::endl;
}
return 0;
}
程序仍然输出为0到41然后退出。这是因为poll方法是非阻塞的,它只是检查当前工作队列中是否有就绪的,有就执行,然后立刻返回。在实际项目中,for循环一般是某种形式的事件循环。上面这个例子也说明了Boost.Asio的事件处理机制,work 对象给io_service对象提供工作内容,但如果在事件处理handler中再给io_service提供work,那么work将会递归无穷的产生,poll将永远不会结束。所以work的添加是在事件处理函数外部进行的。
也就是说可以根据程序的设置,选择采用run()方法还是poll方法来运行程序。当然也有一些变体,run_one(),poll_one(),它们分别表示每次只执行一个事件处理handler,这在并发时能够很好的控制程序。
从io_service.run()从io_service中删除work会使io_service.run()结束阻塞状态,但是io_service并没有这样的成员函数,这时必须通过智能指针来实现:通过reset智能指针来删除智能指针所指向的对象,这样就可以很优雅的移除work了。
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
int main( int argc, char * argv[] )
{
boost::asio::io_service io_service;
boost::shared_ptr< boost::asio::io_service::work > work(
new boost::asio::io_service::work( io_service )
);
work.reset();
io_service.run();
std::cout << "Do you reckon this line displays?" << std::endl;
return 0;
}
io_service支持多线程,在上一篇博文中也提到过,并且说明了io_service的线程调度顺序。下面是一个多线程的例子:
void WorkerThread()
{
std::cout << "Thread Start\n";
io_service.run();
std::cout << "Thread Finish\n";
}
int main( int argc, char * argv[] )
{
boost::shared_ptr< boost::asio::io_service::work > work(
new boost::asio::io_service::work( io_service )
);
std::cout << "Press [return] to exit." << std::endl;
boost::thread_group worker_threads;
for( int x = 0; x < 4; ++x )
{
worker_threads.create_thread( WorkerThread );
}
std::cin.get();
io_service.stop();
worker_threads.join_all();
return 0;
}
上面用到了stop。如果我们想让io_service把当前工作队列中的所有工作执行完毕后再结束,需要用上面提到的reset智能指针的方法而不是stop,而如果采用reset方法的时候不断的往io_service的工作队列中增加新的任务,那么它将永远也不会停下来。