...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
A strand is defined as a strictly sequential invocation of event handlers (i.e. no concurrent invocation). Use of strands allows execution of code in a multithreaded program without the need for explicit locking (e.g. using mutexes).
Strands may be either implicit or explicit, as illustrated by the following alternative approaches:
strand<>
or io_context::strand
.
All event handler function objects need to be bound to the strand using
boost::asio::bind_executor()
or otherwise posted/dispatched through the strand object.
In the case of composed asynchronous operations, such as async_read()
or async_read_until()
,
if a completion handler goes through a strand, then all intermediate handlers
should also go through the same strand. This is needed to ensure thread
safe access for any objects that are shared between the caller and the
composed operation (in the case of async_read()
it's the socket, which the caller can
close()
to cancel the operation).
To achieve this, all asynchronous operations obtain the handler's associated
executor by using the get_associated_executor
function. For example:
boost::asio::associated_executor_t<Handler> a = boost::asio::get_associated_executor(h);
The associated executor must satisfy the Executor requirements. It will be used by the asynchronous operation to submit both intermediate and final handlers for execution.
The executor may be customised for a particular handler type by specifying
a nested type executor_type
and member function get_executor()
:
class my_handler { public: // Custom implementation of Executor type requirements. typedef my_executor executor_type; // Return a custom executor implementation. executor_type get_executor() const noexcept { return my_executor(); } void operator()() { ... } };
In more complex cases, the associated_executor
template may be partially specialised directly:
struct my_handler { void operator()() { ... } }; namespace boost { namespace asio { template <class Executor> struct associated_executor<my_handler, Executor> { // Custom implementation of Executor type requirements. typedef my_executor type; // Return a custom executor implementation. static type get(const my_handler&, const Executor& = Executor()) noexcept { return my_executor(); } }; } } // namespace boost::asio
The boost::asio::bind_executor()
function is a helper to bind a specific executor object, such as a strand,
to a completion handler. This binding automatically associates an executor
as shown above. For example, to bind a strand to a completion handler we
would simply write:
my_socket.async_read_some(my_buffer, boost::asio::bind_executor(my_strand, [](error_code ec, size_t length) { // ... }));
associated_executor, get_associated_executor, bind_executor, strand, io_context::strand, tutorial Timer.5, HTTP server 3 example.