博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

boost asio 介绍 关于strand的介绍不错

Posted on 2016-03-29 09:37  bw_0927  阅读(6636)  评论(0编辑  收藏  举报

http://blog.csdn.net/ithiker/article/details/22153001

 

Boost Asio ( asynchronous input and output)关注异步输入输出。Boost Asio库提供了平台无关性的异步数据处理能力(当然它也支持同步数据处理)。一般的数据传输过程需要通过函数的返回值来判断数据传输是否成功。Boost Asio将数据传输分为两个独立的步骤:

    1. 采用异步任务的方式开始数据传输。
    2. 将传输结果回掉通知调用端

        与传统方式相比,优点在于程序在数据传输期间不会阻塞。

二 I/O services and I/O objects

        应用程序采用Boost.Asio进行异步数据处理主要基于 I/O services 和 I/O objects。I/O services抽象系统I/O接口,提供异步数据传输的能力,它是应用程序和系统I/O接口的桥梁。I/O objects 用来初始化某些特定操作,如TCP socket,提供TCP方面可靠数据传输的能力。Boost.Asio只提供一个类实现 I/O services, boost::asio::io_service。提供多个I/O objects对象,如boost::asio::ip::tcp::socket(用来收发数据)和boost::asio::deadline_timer(用来提供计时器的功能,计时器可以在某个时间点或经历某个时间段后生效)

 

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

void handler1(const boost::system::error_code &ec)
{
  std::cout << "5 s." << std::endl;
}

void handler2(const boost::system::error_code &ec)
{
  std::cout << "5 s." << std::endl;
}

boost::asio::io_service io_service;  //全局的io_service

void run()
{
  io_service.run();
}

int main()
{
  boost::asio::deadline_timer timer1(io_service, boost::posix_time::seconds(5));
  timer1.async_wait(handler1);
  boost::asio::deadline_timer timer2(io_service, boost::posix_time::seconds(5));
  timer2.async_wait(handler2);

//timer1和timer2的handler注册在同一个io_service上,并且两个线程运行在同一个io_service上,所以handler1,handler2可以运行在任意一个线程上
  boost::thread thread1(run);
  boost::thread thread2(run);
  thread1.join();
  thread2.join();
}

 

采用boost::asio::io_service调用run()方法,和boost::asio::io_service相关联的handler将会在同一线程内(调用run()方法的线程内)触发。通过采用多线程,应用程序可以同时调用多个run()方法。一旦某个异步操作完成,对应的I/O service将会执行某个线程中的handler方法。如果第二个异步操作在第一个结束后很快完成,I/O service 可以立刻执行其对应的handler,而不用等待第一个handler执行完毕。

线程可以增加程序的执行效率。因为线程跑在CPU的核上,创建比CPU核数还多的线程是没有意义的。这可以保证每个线程跑在各自的核上,而不会出现多个线程为抢占某个核的”核战争”。

 

应该注意到,采用线程也不是总是合理的。如果它们需要访问共享资源,则需要同步手段。

如果线程各自的handler不能独立的并行执行(handler1的输出可能影响到handler2),在这种场景下使用线程不会带来什么好处

 

 

基于Boost.Asio来提高程序的可扩展性推荐的方法是:采用单个I/O service多次调用run()方法。当然,也有另外的方法可以选择:可以创建多个I/O service,而不是将所有的线程都绑定到一个I/O service上。每个I/O service对应于一个线程【one loop per thread】

如果I/O service的个数和计算机的核数相匹配,异步操作将会在各自对应的核上运行。

 

 

 

==============

http://blog.csdn.net/ithiker/article/details/22600489

#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 work,keep it running

  io_service.run();

  std::cout << "Do you reckon this line displays?" << std::endl;

  return 0;
}

 

如果不喜欢用阻塞的方法(io_service.run())来运行事件处理,Boost.Asio也提供了其它类似BSD socket的任务调度方法,poll就是其中之一。poll调用io_service的事件循环来运行已经就绪的handler方法

poll方法是非阻塞的,它只是检查当前工作队列中是否有就绪的,有就执行,执行完成后立刻返回。

 

 

#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 );

while(1)
{
io_service.poll();
}
std::cout << "end loop: " << std::endl;

return 0;
}

 

从io_service中删除work会使io_service.run()结束阻塞状态,但是io_service并没有这样的成员函数,这时必须通过智能指针来实现:通过reset智能指针来删除智能指针所指向的对象,这样就可以很优雅的移除work了。

 

 

 

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex global_stream_lock;

void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )
{
	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id()
		<< "] Thread Start" << std::endl;
	global_stream_lock.unlock();

	io_service->run();                      //worker线程run()

	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id()
		<< "] Thread Finish" << std::endl;
	global_stream_lock.unlock();
}

void Dispatch( int x )
{
	global_stream_lock.lock();
	std::cout << "[" <<  boost::this_thread::get_id()  << "] "
		<< __FUNCTION__  << " x = " << x <<  std::endl;
	global_stream_lock.unlock();
}

void Post( int x )
{
	global_stream_lock.lock();
	std::cout << "[" <<  boost::this_thread::get_id()  << "] "
		<< __FUNCTION__  << " x = " << x <<  std::endl;
	global_stream_lock.unlock();
}

void Run3( boost::shared_ptr< boost::asio::io_service > io_service )
{
	for( int x = 0; x < 3; ++x )
	{
		io_service->dispatch( boost::bind( &Dispatch, x * 2 ) );
		io_service->post( boost::bind( &Post, x * 2 + 1 ) );
		boost::this_thread::sleep( boost::posix_time::milliseconds( 1000 ) );
	}
}

int main( int argc, char * argv[] )
{
	boost::shared_ptr< boost::asio::io_service > io_service(
		new boost::asio::io_service
	);
	boost::shared_ptr< boost::asio::io_service::work > work(
		new boost::asio::io_service::work( *io_service )
	);

	global_stream_lock.lock();
	std::cout << "[" <<  boost::this_thread::get_id()
		<< "] The program will exit when all  work has finished." <<  std::endl;
	global_stream_lock.unlock();

	boost::thread_group worker_threads;
	for( int x = 0; x < 1; ++x )      //线程个数可调整
	{
		worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );    //一个io_service
	}

	io_service->post( boost::bind( &Run3, io_service ) );        //主线程中Post任务

	work.reset();

	worker_threads.join_all();

	return 0;
}
  •  dispatch()投递任务,如果当前投递任务的线程也调用了io_service.run()方法,则任务会在dispatch()内部被立即执行。(如果调用post()函数的当前线程就是任务所属的线程,任务会被立即执行)) 。

相当于muduo的void EventLoop::runInLoop(const Functor& cb);

http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/reference/io_service/dispatch.html

 

  •    post()只负责投递任务并立即返回,不管当前投递任务的线程是否调用了io_service.run()方法,post()方法的内部都不会执行该任务(即使调用post()函数的当前线程就是任务所属的线程,任务也不会被执行,只进行任务的入队操作)。

相当于muduo的void EventLoop::queueInLoop(const Functor& cb);  

但是还不太一样,因为post()的io_service可能会有多个线程,而queueInLoop特定于当前的单个线程。

 

http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/reference/io_service/post.html

Request the io_service to invoke the given handler and return immediately.  (请求执行(不会立即执行,只是请求io_service在将来的某个时刻执行),并立即返回)

This function is used to ask the io_service to execute the given handler, but without allowing the io_service to call the handler from inside this function.(不允许立即执行)

 

http://www.cnblogs.com/my_life/articles/4806314.html

我们发现,输出没有按照顺序输出,这是因为只有一个线程(x < 1),dispatch在此时不管工作队列中有多少任务都会优先执行(在dispatch内部自动被执行),而post则要等待队列中的其它完成后再执行(post只负责投递任务,

任务何时被执行,取决于执行调用io_service.post()的io_service对象被run()的线程个数,在当前例子中,只有一个线程,所以看上去是等所有dispatch的任务执行完成后才被执行的);

如果线程改为2个或更多,就是顺序输出了,这是因为有sleep的存在,去掉sleep后输出取决于谁先获得锁。如果工作有先后顺序,采用上面的方法显然是不能达到预期的。

多个线程就顺序输出了,这是因为dispatch()立马就能输出,随后的post()可以在其他线程中‘’立马‘’(因为任务少,其他线程的任务队列是空,看上去post的任务得到了立马执行)得到执行,这就顺序了。

 

 

 

默认情况下,一个service多个线程的情况下,你是不知道每个异步handler的调用顺序的。你可以使用service.post()来使你的自定义方法被异步地调用。
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <iostream>
using namespace boost::asio;
io_service service;          //一个service
void func(int i) {
    std::cout << "func called, i= " << i << std::endl;
}

void worker_thread() {
    service.run();
}

int main(int argc, char* argv[]) {
    for ( int i = 0; i < 10; ++i)
        service.post(boost::bind(func, i));
    boost::thread_group threads;
    for ( int i = 0; i < 3; ++i)               //多个线程
        threads.create_thread(worker_thread);
    // 等待所有线程被创建完
    boost::this_thread::sleep( boost::posix_time::millisec(500));
    threads.join_all();
}

service.post(some_function)添加了一个异步方法调用。

这个方法在某一个调用了service.run()的线程中请求io_service实例,然后调用给定的some_funtion之后立即返回。在我们的例子中,这个线程是我们之前创建的三个线程中的一个。你不能确定异步方法调用的顺序。你不要期待它们会以我们调用post()方法的顺序来调用。

 

有时候你会想让一些异步处理方法顺序执行。比如,你去一个餐馆(go_to_restaurant),下单(order),然后吃(eat)。你需要先去餐馆,然后下单,最后吃。这样的话,你需要用到io_service::strand,这个方法会让你的异步方法被顺序调用。

 http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/reference/io_service__strand.html

解决上面的问题,boost提供了strand来给工作排序。strand能够保证handler有序执行,也就是说,如果我们通过strand给io_services post了work1->work2-->work3,不管有多少线程,它们都会按照那个顺序执行。

下面是strand使用的一些规则,这对使用strand至关重要,如果我们不太了解,编写的代码可能会产生未定义的行为,导致运行很长时间,有时程序崩溃也很难排查。

规则:

如果有:strand s;满足完成处理要求的对象a,b;a,b的拷贝a1,b1;  【a1, b1是异步操作的回调handler被bind拷贝出来的】

当下面的任何之一条件成立:

  • s.post(a)早于s.post(b)
  • s.post(a)早于s.dispatch(b),后者在strand外进行
  • s.dispatch(a)早于s.post(b),前者在strand外进行
  • s.dispatch(a)早于s.dispatch(b),两者均在strand外进行

只要有dispatch(), 其就必须在strand外进行才能保证顺序

那么:asio_handler_invoke(a1,&a1)早于asio_handler_invoke(b1,&b1)

但是,如果像下面的情况:

async_op_1(..., s.wrap( a ));
async_op_2(..., s.wrap( b ));

此时,这两个异步操作都会调用对应的dispatch,但是我们不知道dispatch(a)和dispach(b)的顺序。也就是说,上面任何一个条件都不满足,此时就不能决定二者的顺序。

The io_service::strand class provides the ability to post and dispatch handlers with the guarantee that none of those handlers will execute concurrently. 

strand::post(), strand::dispatch()都会保证不会并发,但顺序的保证条件比较严格

The strand object guarantees that handlers posted or dispatched through the strand will not be executed concurrently.

strand保证所有的handler不会被同时执行,但其不保证有序执行。要想顺序就得满足上面提到的四个条件之一。

英文版在这里:

http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=5

Order of handler invocation

Given:

  • a strand object s
  • an object a meeting completion handler requirements
  • an object a1 which is an arbitrary copy of a made by the implementation(异步事件完成时执行的handler拷贝)
  • an object b meeting completion handler requirements
  • an object b1 which is an arbitrary copy of b made by the implementation

if any of the following conditions are true:

  • s.post(a) happens-before s.post(b)    //post一如既往只负责投递任务,然后立即返回
  • s.post(a) happens-before s.dispatch(b), where the latter is performed outside the strand
  • s.dispatch(a) happens-before s.post(b), where the former is performed outside the strand
  • s.dispatch(a) happens-before s.dispatch(b), where both are performed outside the strand

then asio_handler_invoke(a1&a1) happens-before asio_handler_invoke(b1&b1).

Note that in the following case:

async_op_1(..., s.wrap(a));
async_op_2(..., s.wrap(b)); 

the completion of the first async operation will perform s.dispatch(a), and the second will perform s.dispatch(b), but the order in which those are performed is unspecified. That is, you cannot state whether one happens-before the other. Therefore none of the above conditions are met and no ordering guarantee is made.

 

io_service::strand::dispatch   如果可能,dispatch()内部会直接运行handler()

Request the strand to invoke the given handler.

The handler may be executed inside this function if the guarantee can be met. If this function is called from within a handler that was posted or dispatched through the same strand, then the new handler will be executed immediately.

(如果dispatch()是在某个通过同一个strand的触发的回调函数handler内被调用的, 那么新的handler将立即得到执行)

The strand's guarantee is in addition to the guarantee provided by the underlying io_service. The io_service guarantees that the handler will only be called in a thread in which the io_service's run member function is currently being invoked.

 

io_service::strand::post   投递任务,在其内部肯定不会直接运行handler()

Request the strand to invoke the given handler and return immediately.

This function is used to ask the strand to execute the given handler, but without allowing the strand to call the handler from inside this function. 内部不允许strand调用被投递的handler

 

The strand object guarantees that handlers posted or dispatched through the strand will not be executed concurrently. The strand's guarantee is in addition to the guarantee provided by the underlying io_service.

Theio_service guarantees that the handler will only be called in a thread in which the io_service's run member function is currently being invoked.

 

io_service::strand::wrap   内部是调用dispatch(),所以还是无序的

This function is used to create a new handler function object that, when invoked, will automatically pass the wrapped handler to the strand's dispatch function.

       

  下面是例子,注意,去掉了标准输出上的锁,采用多线程:

 

[cpp] view plain copy
 
  1. #include <boost/asio.hpp>  
  2. #include <boost/shared_ptr.hpp>  
  3. #include <boost/thread.hpp>  
  4. #include <boost/thread/mutex.hpp>  
  5. #include <boost/bind.hpp>  
  6. #include <iostream>  
  7.   
  8. boost::mutex global_stream_lock;  
  9.   
  10. void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )  
  11. {  
  12.     global_stream_lock.lock();  
  13.     std::cout << "[" << boost::this_thread::get_id() << "] Thread Start" << std::endl;  
  14.     global_stream_lock.unlock();  
  15.   
  16.     io_service->run();  
  17.   
  18.     global_stream_lock.lock();  
  19.     std::cout << "[" << boost::this_thread::get_id()  
  20.         << "] Thread Finish" << std::endl;  
  21.     global_stream_lock.unlock();  
  22. }  
  23.   
  24. void PrintNum( int x )  
  25. {  
  26.     std::cout << "[" << boost::this_thread::get_id()   
  27.         << "] x: " << x << std::endl;  
  28. }  
  29.   
  30. int main( int argc, char * argv[] )  
  31. {  
  32.     boost::shared_ptr< boost::asio::io_service > io_service(  
  33.         new boost::asio::io_service  
  34.     );  
  35.     boost::shared_ptr< boost::asio::io_service::work > work(  
  36.         new boost::asio::io_service::work( *io_service )  
  37.     );  
  38.     boost::asio::io_service::strand strand( *io_service );  
  39.   
  40.     global_stream_lock.lock();  
  41.     std::cout << "[" <<  boost::this_thread::get_id()    
  42.         << "] The program will exit when all  work has finished." <<  std::endl;  
  43.     global_stream_lock.unlock();  
  44.   
  45.     boost::thread_group worker_threads;  
  46.     forint x = 0; x < 4; ++x )  
  47.     {  
  48.         worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );              //一个service多个线程
  49.     }  
  50.   
  51.     boost::this_thread::sleep( boost::posix_time::milliseconds( 100 ) );  
  52.     io_service->poststrand.wrap( boost::bind( &PrintNum, 1 ) ) );  
  53.     io_service->post( strand.wrap( boost::bind( &PrintNum, 2 ) ) );  
  54.   
  55.     boost::this_thread::sleep( boost::posix_time::milliseconds( 100 ) );  
  56.     io_service->post( strand.wrap( boost::bind( &PrintNum, 3 ) ) );  
  57.     io_service->post( strand.wrap( boost::bind( &PrintNum, 4 ) ) );  
  58.   
  59.     boost::this_thread::sleep( boost::posix_time::milliseconds( 100 ) );  
  60.     io_service->post( strand.wrap( boost::bind( &PrintNum, 5 ) ) );  
  61.     io_service->post( strand.wrap( boost::bind( &PrintNum, 6 ) ) );  
  62.   
  63.     work.reset();  
  64.   
  65.     worker_threads.join_all();  
  66.   
  67.     return 0;  
  68. }  

 

上面都是通过io_service来post事件的(多个线程执行回调),所以不能保证顺序

上面的strand并不能保证按需输出,改为 

  1. strand.post( boost::bind( &PrintNum, 1 ) );  

的形式就可以实现了。

也就是说,在多线程中,如果我们采用下面的A类方法,可以保证顺序,采用B,则不能;这在strand的介绍文档中有专门的说明,是因为strandpost方法保序,而strand wrap到某个方法上并不能保证整体有序。

    1. A:     有序的俩要素:(strand, strand.post)
    2. strand.post( boost::bind( &PrintNum, 1 ) );  
    3. strand.post( boost::bind( &PrintNum, 2 ) );  
    4. strand.post( boost::bind( &PrintNum, 3 ) );  
    5.    
    6. B:  该情况只保证非并发,不保证顺序
    7. io_service->post( strand.wrap( boost::bind( &PrintNum, 1) ) );  
    8. io_service->post( strand.wrap( boost::bind( &PrintNum, 2 ) ) );  
    9. io_service->post( strand.wrap( boost::bind( &PrintNum, 3 ) ) );  

 http://mmoaay.photo/boost.asio/boost-asio-cpp-network-programming-1

 

wrap()返回了一个仿函数,它可以用来做另外一个方法的参数

大多数情况下你只需要把io_service::strand::wrap()方法做为io_service::poll()或者io_service::dispatch()方法的参数即可。

strand::wrap()方法存在的意义就是封装handler,使得其可以投递给io_service,并且保证handler会被线性执行,注意:并非是顺序执行

========================

 

http://blog.csdn.net/ithiker/article/details/23085351

下面介绍Boost.Asio的异常处理和计时器(timer)

 

  1. #include <boost/asio.hpp>  
  2. #include <boost/shared_ptr.hpp>  
  3. #include <boost/thread.hpp>  
  4. #include <boost/thread/mutex.hpp>  
  5. #include <boost/bind.hpp>  
  6. #include <iostream>  
  7.   
  8. boost::mutex global_stream_lock;  
  9.   
  10. void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )  
  11. {  
  12.     global_stream_lock.lock();  
  13.     std::cout << "[" << boost::this_thread::get_id()  
  14.         << "] Thread Start" << std::endl;  
  15.     global_stream_lock.unlock();  
  16.   
  17.     try     //捕获异常
  18.     {  
  19.         io_service->run();  
  20.     }  
  21.     catch( std::exception & ex )  
  22.     {  
  23.         global_stream_lock.lock();  
  24.         std::cout << "[" << boost::this_thread::get_id()  
  25.             << "] Exception: " << ex.what() << std::endl;  
  26.         global_stream_lock.unlock();  
  27.     }  
  28.   
  29.     global_stream_lock.lock();  
  30.     std::cout << "[" << boost::this_thread::get_id()  
  31.         << "] Thread Finish" << std::endl;  
  32.     global_stream_lock.unlock();  
  33. }  
  34.   
  35. void RaiseAnException( boost::shared_ptr< boost::asio::io_service > io_service )  
  36. {  
  37.     global_stream_lock.lock();  
  38.     std::cout << "[" << boost::this_thread::get_id()  
  39.         << "] " << __FUNCTION__ << std::endl;  
  40.     global_stream_lock.unlock();  
  41.   
  42.     io_service->post( boost::bind( &RaiseAnException, io_service ) );    //第二次抛出异常
  43.     io_service->post( boost::bind( &RaiseAnException, io_service ) );    //第三次抛出异常
  44.   
  45.     throw( std::runtime_error( "Oops!" ) );  
  46. }  
  47.   
  48. int main( int argc, char * argv[] )  
  49. {  
  50.     boost::shared_ptr< boost::asio::io_service > io_service(  
  51.         new boost::asio::io_service  
  52.         );  
  53.     boost::shared_ptr< boost::asio::io_service::work > work(  
  54.         new boost::asio::io_service::work( *io_service )  
  55.         );  
  56.   
  57.     global_stream_lock.lock();  
  58.     std::cout << "[" << boost::this_thread::get_id()   
  59.         << "] The program will exit when all work has finished." << std::endl;  
  60.     global_stream_lock.unlock();  
  61.   
  62.     boost::thread_group worker_threads;  
  63.     forint x = 0; x < 2; ++x )    //只创建两个线程
  64.     {  
  65.         worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );  
  66.     }  
  67.   
  68.     io_service->post( boost::bind( &RaiseAnException, io_service ) );    //第一次抛出异常
  69.   
  70.     worker_threads.join_all();  
  71.   
  72.     return 0;  
  73. }  

输出:只捕获了两次

[7f8ed103a720] The program will exit when all work has finished.
[7f8ecf4cd700] Thread Start
[7f8ecf4cd700] RaiseAnException
[7f8ecf4cd700] Exception: Oops!
[7f8ecf4cd700] Thread Finish
[7f8ecfece700] Thread Start
[7f8ecfece700] RaiseAnException
[7f8ecfece700] Exception: Oops!
[7f8ecfece700] Thread Finish

 

错误码的使用

替换WorkerThread()如下:

  1. void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )  
  2. {  
  3.     global_stream_lock.lock();  
  4.     std::cout << "[" << boost::this_thread::get_id()  
  5.         << "] Thread Start" << std::endl;  
  6.     global_stream_lock.unlock();  
  7.   
  8.     boost::system::error_code ec;  
  9.     io_service->run( ec );    //使用错误码,而没使用异常捕获
  10.   
  11.     if( ec )  
  12.     {  
  13.         global_stream_lock.lock();  
  14.         std::cout << "[" << boost::this_thread::get_id()  
  15.             << "] Exception: " << ec << std::endl;  
  16.         global_stream_lock.unlock();  
  17.     }  
  18.   
  19.     global_stream_lock.lock();  
  20.     std::cout << "[" << boost::this_thread::get_id()  
  21.         << "] Thread Finish" << std::endl;  
  22.     global_stream_lock.unlock();  
  23. }  

运行上面的程序会产生core dump,这是因为产生的exception没有被catch。根本原因是因为错误码的方式不会将用户自定义的异常转换为错误码,而只针对Boost.Asio的库才会起作用。

针对Boost.Asio库产生的异常,如果没有使用错误码,将会上抛异常;如果使用了,将会将异常转换为对应的错误码。

 

 

===========timer=====================

timer在第一篇中有过介绍,先看一个的例子:

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include <boost/asio.hpp>  
  2. #include <boost/shared_ptr.hpp>  
  3. #include <boost/thread.hpp>  
  4. #include <boost/thread/mutex.hpp>  
  5. #include <boost/bind.hpp>  
  6. #include <iostream>  
  7.   
  8. boost::mutex global_stream_lock;  
  9.   
  10. void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )  
  11. {  
  12.     global_stream_lock.lock();  
  13.     std::cout << "[" << boost::this_thread::get_id()  
  14.         << "] Thread Start" << std::endl;  
  15.     global_stream_lock.unlock();  
  16.   
  17.     whiletrue )  
  18.     {  
  19.         try  
  20.         {  
  21.             boost::system::error_code ec;  
  22.             io_service->run( ec );  
  23.             if( ec )  
  24.             {  
  25.                 global_stream_lock.lock();  
  26.                 std::cout << "[" << boost::this_thread::get_id()  
  27.                     << "] Error: " << ec << std::endl;  
  28.                 global_stream_lock.unlock();  
  29.             }  
  30.             break;  
  31.         }  
  32.         catch( std::exception & ex )  
  33.         {  
  34.             global_stream_lock.lock();  
  35.             std::cout << "[" << boost::this_thread::get_id()  
  36.                 << "] Exception: " << ex.what() << std::endl;  
  37.             global_stream_lock.unlock();  
  38.         }  
  39.     }  
  40.   
  41.     global_stream_lock.lock();  
  42.     std::cout << "[" << boost::this_thread::get_id()  
  43.         << "] Thread Finish" << std::endl;  
  44.     global_stream_lock.unlock();  
  45. }  
  46.   
  47. void TimerHandler( const boost::system::error_code & error )  
  48. {  
  49.     if( error )  
  50.     {  
  51.         global_stream_lock.lock();  
  52.         std::cout << "[" << boost::this_thread::get_id()  
  53.             << "] Error: " << error << std::endl;  
  54.         global_stream_lock.unlock();  
  55.     }  
  56.     else  
  57.     {  
  58.         global_stream_lock.lock();  
  59.         std::cout << "[" << boost::this_thread::get_id()  
  60.             << "] TimerHandler " << std::endl;  
  61.         global_stream_lock.unlock();  
  62.     }  
  63. }  
  64.   
  65. int main( int argc, char * argv[] )  
  66. {  
  67.     boost::shared_ptr< boost::asio::io_service > io_service(  
  68.         new boost::asio::io_service  
  69.         );  
  70.     boost::shared_ptr< boost::asio::io_service::work > work(  
  71.         new boost::asio::io_service::work( *io_service )  
  72.         );  
  73.   
  74.     global_stream_lock.lock();  
  75.     std::cout << "[" << boost::this_thread::get_id()  
  76.         << "] Press [return] to exit." << std::endl;  
  77.     global_stream_lock.unlock();  
  78.   
  79.     boost::thread_group worker_threads;  
  80.     forint x = 0; x < 2; ++x )  
  81.     {  
  82.         worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );  
  83.     }  
  84.   
  85.     boost::asio::deadline_timer timer( *io_service );  
  86.     timer.expires_from_now( boost::posix_time::seconds( 5 ) );  
  87.     timer.async_wait( TimerHandler );  
  88.   
  89.     std::cin.get();  
  90.   
  91.     io_service->stop();  
  92.   
  93.     worker_threads.join_all();  
  94.   
  95.     return 0;  
  96. }  


很简单,程序等待5秒后运行handler。如果上面的例子我们需要一个可以反复使用的timer,可以把timer做成全局的,但是全局的变量是线程不安全的。这个时候可以用智能指针来解决这一问题:

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include <boost/asio.hpp>  
  2. #include <boost/shared_ptr.hpp>  
  3. #include <boost/thread.hpp>  
  4. #include <boost/thread/mutex.hpp>  
  5. #include <boost/bind.hpp>  
  6. #include <iostream>  
  7.   
  8. boost::mutex global_stream_lock;  
  9.   
  10. void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )  
  11. {  
  12.     global_stream_lock.lock();  
  13.     std::cout << "[" << boost::this_thread::get_id()  
  14.         << "] Thread Start" << std::endl;  
  15.     global_stream_lock.unlock();  
  16.   
  17.     whiletrue )  
  18.     {  
  19.         try  
  20.         {  
  21.             boost::system::error_code ec;  
  22.             io_service->run( ec );  
  23.             if( ec )  
  24.             {  
  25.                 global_stream_lock.lock();  
  26.                 std::cout << "[" << boost::this_thread::get_id()  
  27.                     << "] Error: " << ec << std::endl;  
  28.                 global_stream_lock.unlock();  
  29.             }  
  30.             break;  
  31.         }  
  32.         catch( std::exception & ex )  
  33.         {  
  34.             global_stream_lock.lock();  
  35.             std::cout << "[" << boost::this_thread::get_id()  
  36.                 << "] Exception: " << ex.what() << std::endl;  
  37.             global_stream_lock.unlock();  
  38.         }  
  39.     }  
  40.   
  41.     global_stream_lock.lock();  
  42.     std::cout << "[" << boost::this_thread::get_id()  
  43.         << "] Thread Finish" << std::endl;  
  44.     global_stream_lock.unlock();  
  45. }  
  46.   
  47. void TimerHandler(  
  48.                   const boost::system::error_code & error,  
  49.                   boost::shared_ptr< boost::asio::deadline_timer > timer  //智能指针,可反复使用
  50.                   )  
  51. {  
  52.     if( error )  
  53.     {  
  54.         global_stream_lock.lock();     //多个线程执行io_service.run(),访问全局的cout,需要加锁
  55.         std::cout << "[" << boost::this_thread::get_id()  
  56.             << "] Error: " << error << std::endl;  
  57.         global_stream_lock.unlock();  
  58.     }  
  59.     else  
  60.     {  
  61.         global_stream_lock.lock();  
  62.         std::cout << "[" << boost::this_thread::get_id()  
  63.             << "] TimerHandler " << std::endl;  
  64.         global_stream_lock.unlock();  
  65.   
  66.         timer->expires_from_now( boost::posix_time::seconds( 5 ) );  
  67.         timer->async_wait( boost::bind( &TimerHandler, _1, timer ) );  
  68.     }  
  69. }  
  70.   
  71. int main( int argc, char * argv[] )  
  72. {  
  73.     boost::shared_ptr< boost::asio::io_service > io_service(  
  74.         new boost::asio::io_service  
  75.         );  
  76.     boost::shared_ptr< boost::asio::io_service::work > work(  
  77.         new boost::asio::io_service::work( *io_service )  
  78.         );  
  79.   
  80.     global_stream_lock.lock();  
  81.     std::cout << "[" << boost::this_thread::get_id()  
  82.         << "] Press [return] to exit." << std::endl;  
  83.     global_stream_lock.unlock();  
  84.   
  85.     boost::thread_group worker_threads;  
  86.     forint x = 0; x < 2; ++x )    //多个线程执行io_service.run()
  87.     {  
  88.         worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );  
  89.     }  
  90.   
  91.     boost::shared_ptr< boost::asio::deadline_timer > timer(  
  92.         new boost::asio::deadline_timer( *io_service )  
  93.         );  
  94.     timer->expires_from_now( boost::posix_time::seconds( 5 ) );  
  95.     timer->async_wait( boost::bind( &TimerHandler, _1, timer ) );  
  96.   
  97.     std::cin.get();  
  98.   
  99.     io_service->stop();  
  100.   
  101.     worker_threads.join_all();  
  102.   
  103.     return 0;  
  104. }  


从上面可以发现,通过智能指针结合bind可以将timer反复使用。上面_1是一个占位符,因为TimerHandler需要两个参数,第一个是关于错误码的,第二个是timer类指针。_1表示第一个参数暂时不提供。

 

         对于上面的例子,如果我们在一个线程中(主线程中)启用timer,在另外一个线程中(WorkerThread)执行事件(PrintNum),如果timer的handler方法(TimerHandler)和事件的handler方法(PrintNum)需要访问同一个object,那么当这两个事件同时发生时,就会出现线程不安全的情况,怎么避免呢,还是采用strand。

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include <boost/asio.hpp>  
  2. #include <boost/shared_ptr.hpp>  
  3. #include <boost/thread.hpp>  
  4. #include <boost/thread/mutex.hpp>  
  5. #include <boost/bind.hpp>  
  6. #include <iostream>  
  7.   
  8. boost::mutex global_stream_lock;  
  9.   
  10. void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )  
  11. {  
  12.     global_stream_lock.lock();  
  13.     std::cout << "[" << boost::this_thread::get_id()  
  14.         << "] Thread Start" << std::endl;  
  15.     global_stream_lock.unlock();  
  16.   
  17.     whiletrue )  
  18.     {  
  19.         try  
  20.         {  
  21.             boost::system::error_code ec;  
  22.             io_service->run( ec );  
  23.             if( ec )  
  24.             {  
  25.                 global_stream_lock.lock();  
  26.                 std::cout << "[" << boost::this_thread::get_id()   
  27.                     << "] Error: " << ec << std::endl;  
  28.                 global_stream_lock.unlock();  
  29.             }  
  30.             break;  
  31.         }  
  32.         catch( std::exception & ex )  
  33.         {  
  34.             global_stream_lock.lock();  
  35.             std::cout << "[" << boost::this_thread::get_id()  
  36.                 << "] Exception: " << ex.what() << std::endl;  
  37.             global_stream_lock.unlock();  
  38.         }  
  39.     }  
  40.   
  41.     global_stream_lock.lock();  
  42.     std::cout << "[" << boost::this_thread::get_id()  
  43.         << "] Thread Finish" << std::endl;  
  44.     global_stream_lock.unlock();  
  45. }  
  46.   
  47. void TimerHandler(  
  48.                   const boost::system::error_code & error,   
  49.                   boost::shared_ptr< boost::asio::deadline_timer > timer,   
  50.                   boost::shared_ptr< boost::asio::io_service::strand > strand  
  51.                   )  
  52. {  
  53.     if( error )  //错误,还需要加锁保护cout
  54.     {  
  55.         global_stream_lock.lock();  
  56.         std::cout << "[" << boost::this_thread::get_id()  
  57.             << "] Error: " << error << std::endl;  
  58.         global_stream_lock.unlock();  
  59.     }  
  60.     else  
  61.     {  //无需加锁保护,因为其回调被strand.wrap()线性保护了起来
  62.         std::cout << "[" << boost::this_thread::get_id()  
  63.             << "] TimerHandler " << std::endl;  
  64.   
  65.         timer->expires_from_now( boost::posix_time::seconds( 1 ) );  
  66.         timer->async_wait(   
  67.             strand->wrap( boost::bind( &TimerHandler, _1, timer, strand ) )  
  68.             );  
  69.     }  
  70. }  
  71.   
  72. void PrintNum( int x )  
  73. {  //无需加锁保护cout,因为其是被strand.post()分发的,不会并发执行,同时还是顺序执行的
  74.     std::cout << "[" << boost::this_thread::get_id()  
  75.         << "] x: " << x << std::endl;  
  76.     boost::this_thread::sleep( boost::posix_time::milliseconds( 1000 ) );  
  77. }  
  78.   
  79. int main( int argc, char * argv[] )  
  80. {  
  81.     boost::shared_ptr< boost::asio::io_service > io_service(  
  82.         new boost::asio::io_service  
  83.         );  
  84.     boost::shared_ptr< boost::asio::io_service::work > work(  
  85.         new boost::asio::io_service::work( *io_service )  
  86.         );  
  87.     boost::shared_ptr< boost::asio::io_service::strand > strand(  
  88.         new boost::asio::io_service::strand( *io_service )  
  89.         );  
  90.   
  91.     global_stream_lock.lock();  
  92.     std::cout << "[" << boost::this_thread::get_id()  
  93.         << "] Press [return] to exit." << std::endl;  
  94.     global_stream_lock.unlock();  
  95.   
  96.     boost::thread_group worker_threads;  
  97.     forint x = 0; x < 2; ++x )  
  98.     {  
  99.         worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );  
  100.     }  
  101.   
  102.     boost::this_thread::sleep( boost::posix_time::seconds( 1 ) );  
  103.   
  104. //strand会保证PrintNum不会并发执行,所以PrintNum里cout时并没有加锁保护
  105. //同时strand.post保证PrintNum会按顺序执行
  106.     strand->post( boost::bind( &PrintNum, 1 ) );  
  107.     strand->post( boost::bind( &PrintNum, 2 ) );  
  108.     strand->post( boost::bind( &PrintNum, 3 ) );  
  109.     strand->post( boost::bind( &PrintNum, 4 ) );  
  110.     strand->post( boost::bind( &PrintNum, 5 ) );  
  111.   
  112.     boost::shared_ptr< boost::asio::deadline_timer > timer(  
  113.         new boost::asio::deadline_timer( *io_service )  
  114.         );  
  115.     timer->expires_from_now( boost::posix_time::seconds( 1 ) );   //主线程启动定时器
  116.     timer->async_wait(   //其回调函数TimerHandler会在调用了io_service.run()的其他线程中被执行,TimerHandler()内部也访问了全局的cout,使用strand来保证不会被并发执行,所以TimerHandler()内部也无需加锁保护
  117.         strand->wrap( boost::bind( &TimerHandler, _1, timer, strand ) )  
  118.         );  
  119.   
  120.     std::cin.get();  
  121.   
  122.     io_service->stop();  
  123.   
  124.     worker_threads.join_all();  
  125.   
  126.     return 0;  
  127. }  


运行上面的程序,会发现一个按顺序输出的1 2 3 4 5以及随后输出的TimerHandler,如果没有strand,在输出1 2 3 4 5的过程中可能会出现杂乱的输出,这是因为标准输出std::cout并没有加锁,这就会产生上面说的线程不安全问题,采用strand可以很好的解决这个问题。