This tutorial demonstrates the use of the Boost::asio::strand class to synchronise callback handlers in a multithreaded PR Ogram.
The previous four tutorials avoided the issue of handler synchronisation by calling the Boost::asio::io_service::run () fun Ction from one thread only. As you already know, the ASIO library provides a guarantee that callback handlers would only be called from threads that AR E currently calling Boost::asio::io_service::run (). Consequently, calling Boost::asio::io_service::run () from the only one thread ensures that callback handlers cannot run concur rently.
The single threaded approach are usually the best place-to-start when developing applications using ASIO. The downside is the limitations it places on programs, particularly servers, including:
- Poor responsiveness when handlers can take a long time to complete.
- An inability-to-scale on multiprocessor systems.
If you find yourself running into these limitations, an alternative approach are to has a pool of threads calling Boost::a Sio::io_service::run (). However, as this allows handlers to execute concurrently, we need a method of synchronisation when handlers might is acces Sing a shared, Thread-unsafe resource.
#include<iostream>#include<Boost/Asio.Hpp> #include <boost /thread. Hpp> #include < boost/bind.hpp> #include << span class= "identifier" >boost/date_time/< span class= "identifier" >posix_time/posix_time hpp>
We Start by defining a class called printer , similar to the class in the previous tutorial. This class would extend the previous tutorial by running and the timers in parallel.
Printer{public:
In addition to initialising a pair of Boost::asio::d Eadline_timer Members, the constructor initialises strand_ the member, a N object of type Boost::asio::strand.
An boost::asio::strand guarantees this, for those handlers that is dispatched through it, an executing handler would be Al lowed to complete before the next one is started. This was guaranteed irrespective of the number of threads that was calling Boost::asio::io_service::run (). Of course, the handlers may still execute concurrently with other handlers that were not dispatched through an Boost::asio :: Strand, or were dispatched through a different Boost::asio::strand object.
Printer(Boost::Asio::Io_service&Io):Strand_(Io),timer1_ (io boost::posix_time::< span class= "identifier" >seconds (1timer2_ (ioboost::posix_time ::seconds (1count_ (0 { /span>
When initiating the asynchronous operations, each callback handler is "wrapped" using the Boost::asio::strand object. The Boost::asio::strand::wrap () function returns a new handler that automatically dispatches its contained handler through The Boost::asio::strand object. By wrapping the handlers using the same boost::asio::strand, we is ensuring that they cannot execute concurrently.
Timer1_.Async_wait(Strand_.Wrap(Boost::Bind(&Printer::Print1,This)));Timer2_.Async_wait(Strand_wrap (boost::< span class= "identifier" >bind (&printer: : print2this} ~printer () {std::cout << "Final count is" << count_ << /span>
In a multithreaded program, the handlers for asynchronous operations should is synchronised if they access shared resource S. In this tutorial, the shared resources used by the handlers (and) is and the print1 print2 std::cout count_ data member.
voidPrint1(){If(Count_<10){Std::cout<<"Timer 1:"<<Count_<<"\ n";++Count_;Timer1_.Expires_at(Timer1_.Expires_at()+Boost::Posix_time::Seconds(1));Timer1_.Async_wait(Strand_.Wrap(Boost::Bind(&Printer::Print1,This)));}}voidPrint2(){If(Count_<10){Std::cout<<"Timer 2:"<<Count_<<"\ n";++Count_;Timer2_.Expires_at(Timer2_.Expires_at()+Boost::Posix_time::Seconds(1));Timer2_.Async_wait(Strand_.Wrap(Boost::Bind(&Printer::Print2,This)));}}private: boost::asio::strand strand_boost::asio::< span class= "identifier" >deadline_timer timer1_; boost::asio::< span class= "identifier" >deadline_timer timer2_; int count_;};
The main function now causes boost::asio::io_service::run () to is called from the threads:the main thread and one Additi Onal thread. This is accomplished using a Boost::thread object.
Just as it would with a call from a single thread, concurrent calls to Boost::asio::io_service::run () would continue to exe Cute while there is ' work ' left-to-do. The background thread won't exit until all asynchronous operations has completed.
IntMain(){Boost::Asio::Io_serviceIo;PrinterP(Io);Boost::Threadt (boost::bind (&boost::asio::io_service::< span class= "identifier" >run&ioio. Run (); t. Join (); return 0;}
See the full source listing
timer.5-synchronising Handlers in multithreaded programs