Семафоры могут подходить для передачи сигналов между процессами. Для многопоточного программирования следует избегать семафоров. Если вам нужен эксклюзивный доступ к ресурсу, используйте mutex. Если вам нужно дождаться сигнала, используйте условную переменную.
Даже наиболее часто упоминаемый случай пула ресурсов может быть реализован проще и безопаснее с помощью условной переменной, чем с семафором. Давайте посмотрим на этот случай. Наивная реализация с семафором будет выглядеть (псевдокод):
wait for semaphore to open
take a resource out of the pool
use the resource
put it back to the pool
open the semaphore for one more thread
Первая проблема заключается в том, что семафор не защищает пул от доступа нескольких потоков. Таким образом, требуется другая защита. Пусть это будет замок:
wait for semaphore to open
acquire the lock for the pool
take a resource out of the pool
release the lock
use the resource
acquire the lock
put the resource back to the pool
release the lock
open the semaphore for one more thread
Необходимо принять дополнительные меры для обеспечения того, чтобы пул не был пустым при доступе. Технически возможно получить доступ к пулу в обход семафора, но это нарушит гарантии доступности ресурсов для процедуры получения, описанной выше. Таким образом, пул должен быть доступен только через эту процедуру.
Пока все хорошо, но что, если поток не хочет пассивно ждать ресурса? Можно ли поддерживать неблокирующее получение ресурсов? Это легко, если сам семафор поддерживает неблокирующее получение; в противном случае (например, в Windows) это будет проблематично. Семафор не может быть обойден, потому что это сломало бы случай блокировки. Прохождение через семафор только в том случае, если пул не пуст, может привести к тупиковой ситуации, если выполняется под блокировкой, но как только блокировка снята, результат проверки на пустоту становится бесполезным. Это, вероятно, выполнимо (я не пробовал), но, безусловно, приводит к значительной дополнительной сложности.
С помощью условной переменной это легко решается. Вот псевдокод с получением блокировки:
acquire the lock
while the resource pool is empty,
wait for condition variable to be signaled
take a resource out of the pool
release the lock
use the resource
acquire the lock
put the resource back to the pool
release the lock
signal the condition variable
И в этом случае нет проблем с добавлением неблокирующего сбора:
acquire the lock
if the resource pool is not empty,
take a resource out of the pool
release the lock
if the pool was empty, return
Как вы можете видеть, он даже не нуждается в доступе к условной переменной и не наносит вреда случаю блокировки. Для меня это явно превосходит использование семафора.