Looking in cppreference, it seems to imply a std::binary_semaphore
may be more efficient than a std::mutex
.
Is there any reason not to use a std::binary_semaphore
initialized to 1
instead of a std::mutex
?
Looking in cppreference, it seems to imply a std::binary_semaphore
may be more efficient than a std::mutex
.
Is there any reason not to use a std::binary_semaphore
initialized to 1
instead of a std::mutex
?
There are differences between a std::binary_semaphore
and a std::mutex
which are mentioned in the cppreference documentation (under the Notes section):
Unlike std::mutex a counting_semaphore is not tied to threads of execution - acquiring a semaphore can occur on a different thread than releasing the semaphore, for example. All operations on counting_semaphore can be performed concurrently and without any relation to specific threads of execution, with the exception of the destructor which cannot be performed concurrently but can be performed on a different thread.
Semaphores are also often used for the semantics of signaling/notifying rather than mutual exclusion, by initializing the semaphore with 0 and thus blocking the receiver(s) that try to acquire(), until the notifier "signals" by invoking release(n). In this respect semaphores can be considered alternatives to std::condition_variables, often with better performance.
So I would say there are 2 reasons to use std::mutex
:
In summary:
These are two separate programming constructs.
They do have some potential functional overlap, but this is something which quite common (e.g. you can do with a struct
every-thing you can do with a class
. But they do have some differences and might be used in difference context).
This old post is from some years before C++20 was introduced (and std::binary_semaphore
added), but it contains some additional relevant information.
A side note:
As @interjay commented above, cppreference does not mention the efficiency of std::binary_semaphore
v.s. std::mutex
, but rather that it may be more efficient than std::counting_semaphore
:
Implementations may implement binary_semaphore more efficiently than the default implementation of std::counting_semaphore.
There is no reason to assume a std::binary_semaphore
will be more efficient for implementing mutual exclusion than a std::mutex
. As has been pointed out in the comments, cppreference merely hints at a potential performance difference between the counted and binary semaphore.
In general, mutex and semaphore target different use cases: A semaphore is for signalling, a mutex is for mutual exclusion. Mutual exclusion means you want to make sure that multiple threads cannot execute certain critical sections of code at the same time. std::mutex
is the only synchronization facility in the standard library for this use case. The semaphore on the other hand targets the use case where one thread causes the program state to change and now wants to inform another thread of that change. There are multiple similar facilities in the standard library that target signalling use cases, for example std::condition_variable
.
This is also the reason why semaphores do not require to be acquired and released on the same thread, unlike mutexes where unlocking a mutex that is held by another thread results in undefined behavior.
You should avoid using a signalling primitive for implementing mutual exclusion. Even though this can in theory be done, it is very easy to do it subtly wrong and likely to be less efficient than a dedicated mutual exclusion algorithm.
For example, the Linux kernel documentation for generic mutexes says,
Mutexes are sleeping locks which behave similarly to binary semaphores, and were introduced in 2006 as an alternative to these. This new data structure provided a number of advantages, including simpler interfaces, and at that time smaller code (see Disadvantages).
To wit:
Disadvantages
Unlike its original design and purpose,
struct mutex
is among the largest locks in the kernel. E.g: on x86-64 it is 32 bytes, wherestruct semaphore
is 24 bytes andrw_semaphore
is 40 bytes. Larger structure sizes mean more CPU cache and memory footprint.
binary_semaphore
is more efficient thanmutex
. It says it may be more efficient than a non-binarycounting_semaphore
.