Serializing workload with strands
A strand is a strict sequential and non-concurrent invocation of completion handlers. Using strands, asynchronous operations can be sequenced without explicit locking by using mutexes or other synchronization mechanisms seen earlier in this book. Strands can be implicit or explicit.
As shown earlier in this chapter, if we execute boost::asio::io_context::run()
from only one thread, all event handlers will execute in an implicit strand, as they will be sequentially queued one by one and triggered from the I/O execution context.
Another implicit strand happens when there are chained asynchronous operations where one asynchronous operation schedules the next asynchronous operation, and so on. Some previous examples in this chapter already used this technique, but here there is another one.
In this case, if there are no errors, the timer keeps restarting itself in the handle_timer_expiry()
event handler by recursively setting up the expiration...