13

I am not sure about one detail related to strands.

Suppose the following situation: two independent objects each one with his own strand. And each strand related to one common io_service. Each object use his strand for posting and wrapping async operations. If I have this (unique) io_service .run()'ing on several threads, I am not sure if the following will happen:

  1. All operations posted and async wrapped by one of the objects will be executed non concurrently. So all operations related to one of the objects will be executed serially (Posted operations will be executed in the same order than they were posted. Wrapped async operations will be executed in an unspecified order because they are asynchronous but still being executed serially).

  2. Two operations originated in different objects (and therefore posted or wrapped from different strand objects related to the same io_service) could be executed concurrently.

  3. In summary, each object will execute his posted and wrapped handlers serially but handlers posted and wrapped from different objects (strands) will execute concurrently.

       +-----------------+  +-----------------+
       | Obj1            |  | Obj2            |
       | +-------------+ |  | +-------------+ |             
       | |   Strand_1  | |  | |   Strand_2  | |               
       | +-------------+ |  | +-------------+ |                
       +--------+--------+  +-------+---------+                
                |                   |                          
                +--------+  +-------+                          
                         |  |                                         
                    +----+--+----+                                      
                    | io_service |                                      
                    +------------+                                      
                           |                                          
                           |                                          
                  +--------+-------+                         
                  |                |                                
             Thread1             Thread_2  
             io_service.run()    io_service.run()                              
    

Am I right?

Thank you

ChrisPeterson
  • 419
  • 4
  • 15

2 Answers2

8

In short, a strand guarantees sequential invocation of its own handlers, but makes no guarantee of concurrent execution of handlers from different strands. Thus, the answers to the points are:

  1. Yes. Sequential invocation is guaranteed.
  2. Yes. Concurrent execution could happen, but there is no guarantee.
  3. Yes to sequential invocation, but no to the the guarantee that concurrent execution will occur.

A strand maintains its own handler queue, and guarantees that only one of its handlers is in the io_service, resulting in handlers being synchronized before being placed into the io_service. Thus, all handlers posted or dispatched through a strand will be executed sequentially.

Concurrent execution of handlers posted or dispatched through different strands can occur, it is just not guaranteed to occur. The documentation states:

The implementation makes no guarantee that handlers posted or dispatched through different strand objects will be invoked concurrently.

Therefore, if Thread1 is executing a handler posted through Strand_1, Boost.Asio will not use that information to guarantee that a handler posted through Strand_2 will be executed by Thread2; however, it is possible that Thread2 is selected to execute the handler from Strand_2 based on other implementation details, such as being the next available thread in the list of threads running the io_service.

For example, consider the case where 3 handlers A, B, and C are ready to run within the io_service:

  • A was posted posted through Strand_1.
  • B was not posted through a strand.
  • C was posted through Strand_2.

If Thread1 and Thread2 are running the io_service, then one possible execution order is:

Thread1         | Thread2
----------------+----------------
start A()       | start B()
`-- finish A()  | |
start C()       | `-- finish B()
`-- finish C()  |

The illustrated execution order shows that that handlers (A and C) posted through different strands (Strand_1 and Strand_2 respectively) are not guaranteed to be executed concurrently.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
  • Thank you. Would it be possible to force some concurrency? I don't quite understand that if a io_service is running in more than one thread and a handler needs to be executed why not use one of 'free' threads. I mean, is it pointless running the io_service in several threads (assuming an scheme as described in my question)? – ChrisPeterson Dec 04 '13 at 16:57
  • 1
    "makes no guarantee", in the context of concurrently invoke handlers posted or dispatched through different strand objects, means that will happen most of the time but not always? Is it worthy to run io_service in several threads so some performance will be gained in the original scheme? – ChrisPeterson Dec 04 '13 at 17:06
  • 1
    @ChrisPeterson I updated the answer to hopefully provide more clarification as to what the guarantee (or lack thereof) provides. If there is a thread available, then Boost.Asio will use it to execute handlers. If multiple handlers need to be invoked concurrently, then multiple threads may be helpful. – Tanner Sansbury Dec 04 '13 at 19:12
  • 1
    Very nice writeup Tanner, +1. One question - if we have multiple threads that use same io_service, and all completion handlers are wrapped into the same strand, is this equivalent to the situation where we have only a single thread? if I am not mistaken, all handlers should be invocated sequentially, so there are no benefits of using multiple threads? – Miljen Mikic Jul 24 '17 at 10:59
  • @MiljenMikic I have the same question with you, and i think so. Actually, when we use only one thread for running all handlers, the handlers were sequentially executed by which was called *[implicit strand](https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/overview/core/strands.html)*. – cifer Nov 12 '18 at 06:27
1

All operations posted and async wrapped by one of the objects will be executed non concurrently. So all operations related to one of the objects will be executed serially (Posted operations will be executed in the same order than they were posted. Wrapped async operations will be executed in an unspecified order because they are asynchronous but still being executed serially).

Yes.

Two operations originated in different objects (and therefore posted or wrapped from different strand objects related to the same io_service) could be executed concurrently.

Yes

In summary, each object will execute his posted and wrapped handlers serially but handlers posted and wrapped from different objects (strands) will execute concurrently.

Yes

Drax
  • 12,682
  • 7
  • 45
  • 85