3

Somehow a followup of this question. I am only wondering if it is ok to use a std::mutex in functions handeled by a boost::asio:io_service? Usage of strands is somwhat unpractical. From what I found in the boost reference I would say it is ok. Since it states that

Asynchronous completion handlers will only be called from threads that are currently calling io_service::run().

So other threads created by boost should not interfere. Did I get it right?

Community
  • 1
  • 1
Haatschii
  • 9,021
  • 10
  • 58
  • 95

4 Answers4

12

As others have noted, std::mutex and other locking mechanics, can be be used within handlers. However, there is a fundamental difference between the two:

  • An external locking mechanism within a handler is used to protect resources from race conditions.
  • A strand is used to remove contention between handlers, resulting in the removal of race conditions between handlers.

If the entire handler is being synchronized as a result of potential race conditions with other handlers, and not threads external to the threadpool, then I would like to accentuate one of the subtle differences in synchronization between an external mechanism and boost::asio::strand.

Consider the following scenario:

  • A threadpool of 2 threads is implemented with Boost.Asio.
  • Handlers A and B will be synchronized on the same mutex.
  • Handler C requires no synchronization.
  • Handlers A, B, and C are posted into the io_service.

A and B are invoked. The threadpool is now exhausted due to external synchronization, as both threads are being used. Unfortunately, one of the threads is blocked on a resource, causing handlers that require no synchronization, such as C, to sit in the queue.

If a strand is used for synchronization in this scenario, then this instance of starvation would not have occurred. 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. In the scenario, if A and B are posted into the strand, then the strand will post A into the io_service. This would result in A and C being in the io_service, allowing C to run concurrently while B remains in the strand's queue waiting for A to complete.

Also, there are use cases where both of these forms of synchronization can be used together. For example, consider the case where resources are shared with a thread running outside of the threadpool. A mutex would still be required by threads internal and external to the threadpool. However, a strand could be used to remove the contention for the mutex between threads internal to the threadpool.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
  • Good point indeed. In my case I want to use a mix of strand and mutex because I have some large functions that at some point require shared resources. But I think it would be an overkill to block the resources for the whole run of the function. Splitting the functions into lots of small ones posted one after another to asio would significantly increase the overhead of the asio thread pool, I guess. – Haatschii Jan 14 '13 at 16:11
8

Yes, using a std::mutex inside of a handler is perfectly fine. A strand is just a queue with a mutex in disguise after all.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
2

boost is simply calling a callback from its perspective. This callback has no relation to boost, so boost doesn't care what you do in the callback. So taking a lock (using any locking library you desire), is perfectly fine.

JaredC
  • 5,150
  • 1
  • 20
  • 45
0

Mutex inside completion handler can block thread execution. In that case you need more io_service threads than boost::thread::hardware_concurrency() to load CPU for 100%. It increases thread switching overhead.

stan5
  • 81
  • 1
  • 4