11

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?

2
  • 4
    cppreference doesn't say that binary_semaphore is more efficient than mutex. It says it may be more efficient than a non-binary counting_semaphore.
    – interjay
    Commented14 hours ago
  • One reason would be to avoid confusing people who are reading the code; if your code is implementing mutual exclusion with semaphores, readers will wonder why it isn't just using mutexes, and will have to spend time figuring out what the implications of that choice are.Commented10 hours ago

3 Answers 3

14

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:

  1. If it's important for you that only the same thread that acquired it will be able to release it.
  2. If you want semantically to show the intent for mutual exclusion rather than signalling.

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.

    7

    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.

      0

      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, where struct semaphore is 24 bytes and rw_semaphore is 40 bytes. Larger structure sizes mean more CPU cache and memory footprint.

        Start asking to get answers

        Find the answer to your question by asking.

        Ask question

        Explore related questions

        See similar questions with these tags.