I'd like to use the new standard threads instead of boost:threads but I've noticed the old shared_mutex is not available. What would be a good recommendation to replace this functionality and give me a multiple-readers, single-writer lock?
-
1I think a better duplicate would be https://stackoverflow.com/q/14306797/783510. The current duplicate asks for a hand-rolled implementation, while the other asks for a standardized read/writer lock. – Philipp Claßen Aug 09 '17 at 01:20
2 Answers
std::shared_mutex
will be part of the C++14 Standard Library. It did not make it to C++11 just because there was no time to formulate a proposal and discuss it thoroughly.
You can still use boost::shared_mutex
though. Under Windows, if you are working with Windows Vista or later, you can use Slim Read-Write Locks, which are optimized for speed and memory consumption.

- 124,023
- 23
- 387
- 451
-
-
10[std::shared_mutex](http://en.cppreference.com/w/cpp/thread/shared_mutex) is not in C++14, it will be in C++17. – Deqing Aug 20 '16 at 11:21
You should take a look at the stack overflow question "C++11 equivalent to boost shared_mutex", and in particular the following linked email conversation: http://permalink.gmane.org/gmane.comp.lib.boost.devel/211180 (which explains the resistance of the C++11 committee to approving shared_mutex). Also the following experiment on Joe Duffy's weblog: http://www.bluebytesoftware.com/blog/2009/02/12/ReaderwriterLocksAndTheirLackOfApplicabilityToFinegrainedSynchronization.aspx.
Every time you are considering a reader/writer lock, ask yourself the following 6 questions. If you can answer "no" to any of them then reader/writer locks are going to make your program worse, not better.
- Is my shared object
const
? I have seen more incorrect uses ofshared_mutex
in my life than correct uses. To use ashared_mutex
correctly it must be the case that you can declare your shared objectsconst
inside the reader critical section without any compiler complaints. A "consumer" is not equivalent to "someone who does not mutate the data structure at all." - Are my critical sections really long? Locking a shared_mutex is much more expensive than locking a regular mutex. You have to have a lot of work in your critical section to make up for the increased overhead of the lock acquire/release.
- Should my critical sections be that long? You should ask yourself whether you really need to be doing all that work in a critical section. Often there is a bunch of preparatory work and/or work to massage the return object surrounding the
const
calls to the shared object. Much of that extra work that isn't on the data-dependence path from the first use of the shared object to the last use of the shared object can be moved outside the critical section. - Is lock contention really my performance problem? Even if your critical sections are long, you should be absolutely sure that lock contention is really your performance problem. If you aren't experiencing significant lock contention then switching to reader/writer locks isn't going to buy you anything.
- Could I reduce my lock contention by switching to a finer-grain locking scheme? Are you using a single lock to protect multiple objects? Can you give each object its own lock?
- Is the ratio of readers to writers significantly greater than 1:1? Even if your critical sections are long and lock contention is a serious problem the ratio of readers to writers needs to be extremely high to get any benefit from reader/writer locks. The amount depends on costs for atomic instructions on your hardware and the quality of the particular implementations. (Joe Duffy finds that on his machine he needed a ratio around 20:1 readers:writers to make reader/writer locks a win.)

- 1
- 1

- 3,323
- 1
- 20
- 25
-
4Though that pageby Joe Duffy needs to be read with a bit of caution. The assumptin that because _one_ implementation of _one_ particular type of rw-lock (in .NET) behaves in some way has not much bearing on rw-locks in general. A rw-lock does not _have to_ be any slower than a critical section object and it does not _have to_ use CAS operations. There are so many variations, for example it doesn't _have to_ be fair (or, it _might_ have to). Depending on the situation, rw-locks can be extremely more efficient (2-3 orders of magnitude) than a mutex. The take on the r/w ratio is funny too... – Damon May 27 '13 at 16:53
-
3... The take on the 1 to 20 ratio is funny, too. It is not really something particularly special. Using the wrong tool for a task certainly gives at best mediocre results. This is like complaining that using `std::list` performs poorly on random access, or `std::vector` performs poorly when inserting millions of elements at the front. If your reads do _not_ outnumber your writes, well, then of course rw-locks are no win (and possibly a loss). But that's using the wrong tool. – Damon May 27 '13 at 16:58
-
1Duffy's example of one request coming in every 0.25ms (= 250,000ns) is a good one too. This is a "totally zero congestion" case. Even thinking about the excessive cost of a CAS operation (which is typically less than 25ns) strikes me funny in that context. A modern CPU is not challenged by doing 4000 atomic operations per second. – Damon May 27 '13 at 17:13
-
@Damon: Yes, you need to choose the appropriate tool for the job. The appropriate tool for the job is a rw-mutex whenever you can answer "yes" to all 6 of my questions. Otherwise it is the wrong tool. Joe Duffy is showing the same thing. You are right that there is nothing important about the 20:1 ratio. I'll edit the post to reflect that. – Wandering Logic May 27 '13 at 17:24
-
1@WanderingLogic could you elaborate on the first point please ? What _is_ a consumer then ? Why is compile-time const-strictness required ? – aberaud Feb 27 '14 at 00:31
-
1@Wagaf: I probably didn't word that as clearly as I should have. I was trying to contrast reader/writer patterns from producer/consumer patterns. An example of a producer/consumer pattern would be a work queue: the producer inserts items into the queue, the consumer removes items from the queue. "Removal" is a mutating operation, so it's not the same as "reading". – Wandering Logic Feb 27 '14 at 12:36
-
2As to compile-time const strictness. I wasn't trying to say that you actually must declare your object const. Rather, you just need to make sure that threads always have the writer lock whenever they mutate the shared object. You need to be completely aware of which methods mutate your shared object. (That is, you have to understand the _implementation_ of every non-const method that you call on the shared object outside of the writer lock.) – Wandering Logic Feb 27 '14 at 12:46