123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- //
- // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- /**
- \page tuttimer1 Timer.1 - Using a timer synchronously
- This tutorial program introduces asio by showing how to perform a blocking
- wait on a timer.
- \dontinclude timer1/timer.cpp
- \skip #include
- We start by including the necessary header files.
- All of the asio classes can be used by simply including the <tt>"asio.hpp"</tt>
- header file.
- \until asio.hpp
- All programs that use asio need to have at least one boost::asio::io_context object.
- This class provides access to I/O functionality. We declare an object of this
- type first thing in the main function.
- \until boost::asio::io_context
- Next we declare an object of type boost::asio::steady_timer. The core asio classes
- that provide I/O functionality (or as in this case timer functionality) always
- take a reference to an io_context as their first constructor argument. The
- second argument to the constructor sets the timer to expire 5 seconds from now.
- \until boost::asio::steady_timer
- In this simple example we perform a blocking wait on the timer.
- That is, the call to boost::asio::steady_timer::wait() will not return until the
- timer has expired, 5 seconds after it was created (i.e. <b>not</b> from when the
- wait starts).
- A timer is always in one of two states: "expired" or "not expired". If the
- boost::asio::steady_timer::wait() function is called on an expired timer, it will
- return immediately.
- \until wait
- Finally we print the obligatory <tt>"Hello, world!"</tt>
- message to show when the timer has expired.
- \until }
- See the \ref tuttimer1src "full source listing" \n
- Return to the \ref index "tutorial index" \n
- Next: \ref tuttimer2
- */
- /**
- \page tuttimer1src Source listing for Timer.1
- \include timer1/timer.cpp
- Return to \ref tuttimer1
- */
- /**
- \page tuttimer2 Timer.2 - Using a timer asynchronously
- This tutorial program demonstrates how to use asio's asynchronous callback
- functionality by modifying the program from tutorial Timer.1 to perform an
- asynchronous wait on the timer.
- \dontinclude timer2/timer.cpp
- \skip #include
- \until asio.hpp
- Using asio's asynchronous functionality means having a callback
- function that will be called when an asynchronous operation completes. In this
- program we define a function called <tt>print</tt> to be called when the
- asynchronous wait finishes.
- \until boost::asio::steady_timer
- Next, instead of doing a blocking wait as in tutorial Timer.1,
- we call the boost::asio::steady_timer::async_wait() function to perform an
- asynchronous wait. When calling this function we pass the <tt>print</tt>
- callback handler that was defined above.
- \skipline async_wait
- Finally, we must call the boost::asio::io_context::run() member function
- on the io_context object.
- The asio library provides a guarantee that callback handlers will <b>only</b>
- be called from threads that are currently calling boost::asio::io_context::run().
- Therefore unless the boost::asio::io_context::run() function is called the callback for
- the asynchronous wait completion will never be invoked.
- The boost::asio::io_context::run() function will also continue to run while there is
- still "work" to do. In this example, the work is the asynchronous wait on the
- timer, so the call will not return until the timer has expired and the
- callback has completed.
- It is important to remember to give the io_context some work to do before
- calling boost::asio::io_context::run(). For example, if we had omitted the above call
- to boost::asio::steady_timer::async_wait(), the io_context would not have had any
- work to do, and consequently boost::asio::io_context::run() would have returned
- immediately.
- \skip run
- \until }
- See the \ref tuttimer2src "full source listing" \n
- Return to the \ref index "tutorial index" \n
- Previous: \ref tuttimer1 \n
- Next: \ref tuttimer3
- */
- /**
- \page tuttimer2src Source listing for Timer.2
- \include timer2/timer.cpp
- Return to \ref tuttimer2
- */
- /**
- \page tuttimer3 Timer.3 - Binding arguments to a handler
- In this tutorial we will modify the program from tutorial Timer.2 so that the
- timer fires once a second. This will show how to pass additional parameters to
- your handler function.
- \dontinclude timer3/timer.cpp
- \skip #include
- \until bind.hpp
- To implement a repeating timer using asio you need to change
- the timer's expiry time in your callback function, and to then start a new
- asynchronous wait. Obviously this means that the callback function will need
- to be able to access the timer object. To this end we add two new parameters
- to the <tt>print</tt> function:
- \li A pointer to a timer object.
- \li A counter so that we can stop the program when the timer fires for the
- sixth time.
- \until {
- As mentioned above, this tutorial program uses a counter to
- stop running when the timer fires for the sixth time. However you will observe
- that there is no explicit call to ask the io_context to stop. Recall that in
- tutorial Timer.2 we learnt that the boost::asio::io_context::run() function completes
- when there is no more "work" to do. By not starting a new asynchronous wait on
- the timer when <tt>count</tt> reaches 5, the io_context will run out of work and
- stop running.
- \until ++
- Next we move the expiry time for the timer along by one second
- from the previous expiry time. By calculating the new expiry time relative to
- the old, we can ensure that the timer does not drift away from the
- whole-second mark due to any delays in processing the handler.
- \until expires_at
- Then we start a new asynchronous wait on the timer. As you can
- see, the boost::bind() function is used to associate the extra parameters
- with your callback handler. The boost::asio::steady_timer::async_wait() function
- expects a handler function (or function object) with the signature
- <tt>void(const boost::system::error_code&)</tt>. Binding the additional parameters
- converts your <tt>print</tt> function into a function object that matches the
- signature correctly.
- See the <a href="http://www.boost.org/libs/bind/bind.html">Boost.Bind
- documentation</a> for more information on how to use boost::bind().
- In this example, the boost::asio::placeholders::error argument to boost::bind() is a
- named placeholder for the error object passed to the handler. When initiating
- the asynchronous operation, and if using boost::bind(), you must specify only
- the arguments that match the handler's parameter list. In tutorial Timer.4 you
- will see that this placeholder may be elided if the parameter is not needed by
- the callback handler.
- \until boost::asio::io_context
- A new <tt>count</tt> variable is added so that we can stop the
- program when the timer fires for the sixth time.
- \until boost::asio::steady_timer
- As in Step 4, when making the call to
- boost::asio::steady_timer::async_wait() from <tt>main</tt> we bind the additional
- parameters needed for the <tt>print</tt> function.
- \until run
- Finally, just to prove that the <tt>count</tt> variable was
- being used in the <tt>print</tt> handler function, we will print out its new
- value.
- \until }
- See the \ref tuttimer3src "full source listing" \n
- Return to the \ref index "tutorial index" \n
- Previous: \ref tuttimer2 \n
- Next: \ref tuttimer4
- */
- /**
- \page tuttimer3src Source listing for Timer.3
- \include timer3/timer.cpp
- Return to \ref tuttimer3
- */
- /**
- \page tuttimer4 Timer.4 - Using a member function as a handler
- In this tutorial we will see how to use a class member function as a callback
- handler. The program should execute identically to the tutorial program from
- tutorial Timer.3.
- \dontinclude timer4/timer.cpp
- \skip #include
- \until bind.hpp
- Instead of defining a free function <tt>print</tt> as the
- callback handler, as we did in the earlier tutorial programs, we now define a
- class called <tt>printer</tt>.
- \until public
- The constructor of this class will take a reference to the
- io_context object and use it when initialising the <tt>timer_</tt> member. The
- counter used to shut down the program is now also a member of the class.
- \until {
- The boost::bind() function works just as well with class
- member functions as with free functions. Since all non-static class member
- functions have an implicit <tt>this</tt> parameter, we need to bind
- <tt>this</tt> to the function. As in tutorial Timer.3, boost::bind()
- converts our callback handler (now a member function) into a function object
- that can be invoked as though it has the signature <tt>void(const
- boost::system::error_code&)</tt>.
- You will note that the boost::asio::placeholders::error placeholder is not specified
- here, as the <tt>print</tt> member function does not accept an error object as
- a parameter.
- \until }
- In the class destructor we will print out the final value of
- the counter.
- \until }
- The <tt>print</tt> member function is very similar to the
- <tt>print</tt> function from tutorial Timer.3, except that it now operates on
- the class data members instead of having the timer and counter passed in as
- parameters.
- \until };
- The <tt>main</tt> function is much simpler than before, as it
- now declares a local <tt>printer</tt> object before running the io_context as
- normal.
- \until }
- See the \ref tuttimer4src "full source listing" \n
- Return to the \ref index "tutorial index" \n
- Previous: \ref tuttimer3 \n
- Next: \ref tuttimer5 \n
- */
- /**
- \page tuttimer4src Source listing for Timer.4
- \include timer4/timer.cpp
- Return to \ref tuttimer4
- */
- /**
- \page tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs
- This tutorial demonstrates the use of the boost::asio::strand class template to
- synchronise callback handlers in a multithreaded program.
- The previous four tutorials avoided the issue of handler synchronisation by
- calling the boost::asio::io_context::run() function from one thread only. As you
- already know, the asio library provides a guarantee that callback handlers will
- <b>only</b> be called from threads that are currently calling
- boost::asio::io_context::run(). Consequently, calling boost::asio::io_context::run() from
- only one thread ensures that callback handlers cannot run concurrently.
- The single threaded approach is usually the best place to start when
- developing applications using asio. The downside is the limitations it places
- on programs, particularly servers, including:
- <ul>
- <li>Poor responsiveness when handlers can take a long time to complete.</li>
- <li>An inability to scale on multiprocessor systems.</li>
- </ul>
- If you find yourself running into these limitations, an alternative approach
- is to have a pool of threads calling boost::asio::io_context::run(). However, as this
- allows handlers to execute concurrently, we need a method of synchronisation
- when handlers might be accessing a shared, thread-unsafe resource.
- \dontinclude timer5/timer.cpp
- \skip #include
- \until bind.hpp
- We start by defining a class called <tt>printer</tt>, similar
- to the class in the previous tutorial. This class will extend the previous
- tutorial by running two timers in parallel.
- \until public
- In addition to initialising a pair of boost::asio::steady_timer members, the
- constructor initialises the <tt>strand_</tt> member, an object of type
- boost::asio::strand<boost::asio::io_context::executor_type>.
- The boost::asio::strand class template is an executor adapter that guarantees
- that, for those handlers that are dispatched through it, an executing handler
- will be allowed to complete before the next one is started. This is guaranteed
- irrespective of the number of threads that are calling
- boost::asio::io_context::run(). Of course, the handlers may still execute
- concurrently with other handlers that were <b>not</b> dispatched through an
- boost::asio::strand, or were dispatched through a different boost::asio::strand
- object.
- \until {
- When initiating the asynchronous operations, each callback handler is "bound"
- to an boost::asio::strand<boost::asio::io_context::executor_type> object. The
- boost::asio::bind_executor() function returns a new handler that automatically
- dispatches its contained handler through the boost::asio::strand object. By
- binding the handlers to the same boost::asio::strand, we are ensuring that they
- cannot execute concurrently.
- \until }
- \until }
- In a multithreaded program, the handlers for asynchronous
- operations should be synchronised if they access shared resources. In this
- tutorial, the shared resources used by the handlers (<tt>print1</tt> and
- <tt>print2</tt>) are <tt>std::cout</tt> and the <tt>count_</tt> data member.
- \until };
- The <tt>main</tt> function now causes boost::asio::io_context::run() to
- be called from two threads: the main thread and one additional thread. This is
- accomplished using an boost::thread object.
- Just as it would with a call from a single thread, concurrent calls to
- boost::asio::io_context::run() will continue to execute while there is "work" left to
- do. The background thread will not exit until all asynchronous operations have
- completed.
- \until }
- See the \ref tuttimer5src "full source listing" \n
- Return to the \ref index "tutorial index" \n
- Previous: \ref tuttimer4 \n
- */
- /**
- \page tuttimer5src Source listing for Timer.5
- \include timer5/timer.cpp
- Return to \ref tuttimer5
- */
|