Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use a mutex and not a semaphore?

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?

like image 609
Baruch Avatar asked Sep 12 '25 17:09

Baruch


2 Answers

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 some reasons to use std::mutex:

  1. First and foremost - if you want semantically to show the intent for mutual exclusion rather than signalling.
  2. Releasing a mutex by a thread different than the one which acquired it is not allowed with a mutex - the result is UB (undefined behavior). Normally this is actually a drawback. But in certain cases you if you have UB detection tools, using a mutex might help you prevent an errounous release by a different thread that was not supposed to do that.

In addition - as @MatthieuM. commented below, mutex implementations might be able to offer a performance advantage over a semaphore, e.g. - Futex implementation on Linux.

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 everything 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.

like image 88
wohlstad Avatar answered Sep 14 '25 08:09

wohlstad


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.

like image 31
ComicSansMS Avatar answered Sep 14 '25 08:09

ComicSansMS