От чего зависит, сможет ли поток пройти через std::counting_semaphore::acquire?
Поток сможет продолжить выполнение, если значение счётчика семафора больше нуля. Успешный acquire атомарно уменьшает счётчик на единицу; если счётчик равен нулю, поток блокируется до появления разрешения.
Семафор появился как примитив синхронизации для управления доступом к ограниченному числу одинаковых ресурсов. В отличие от мьютекса, он не закрепляет ресурс за конкретным владельцем, а хранит количество доступных разрешений.
В C++20 стандартная библиотека получила std::counting_semaphore и std::binary_semaphore. Это позволило выражать ожидание доступного ресурса без ручной реализации счётчика, условной переменной и мьютекса.
Если несколько потоков используют ограниченный пул соединений, задач или буферных слотов, обычный мьютекс сообщает только факт занятости ресурса. Он не выражает, что одновременно доступны, например, четыре независимых разрешения.
Ошибочное представление о семафоре как о мьютексе приводит к неверным гарантиям: семафор не отслеживает владельца, его можно освободить из другого потока, а успешно выполненный acquire не означает владение конкретным объектом.
std::counting_semaphore<N> содержит счётчик в диапазоне, поддерживаемом реализацией и ограниченном N. Начальное значение задаётся при создании. Каждый успешный acquire уменьшает его, а release увеличивает; если значение было нулевым, release может разблокировать ожидающий поток.
Минимальный пример передачи разрешения вместе с публикацией данных:
В этом примере успешный acquire, разблокированный соответствующим release, устанавливает отношение happens-before: запись result становится видимой после acquire. Поэтому атомик или мьютекс для самого result не нужны, если отсутствуют другие конкурентные обращения и соблюдается такая передача.
Начальное разрешение, созданное в семафоре до запуска потоков, не следует путать с разрешением, опубликованным другим потоком: для исходной записи в обычные данные нужна отдельная гарантия межпоточной публикации, например создание потока, join или операция синхронизации.
try_acquire не блокирует поток и возвращает управление, если разрешения нет. acquire не предоставляет тайм-аут; для ограничения времени ожидания применяют try_acquire_for или try_acquire_until. Максимальное значение счётчика нельзя бездумно превышать вызовами release, иначе поведение не соответствует ограничениям семафора.
Главный компромисс — простота против контроля. Семафор удобен для ограничения параллелизма, но не защищает составное состояние сам по себе и не гарантирует справедливый порядок обслуживания ожидающих потоков.
Сервис обрабатывает запросы через пул из восьми внешних соединений. Вариант с одним мьютексом защищает структуру пула, но не выражает ограничение на восемь одновременных операций. Вариант с восемью отдельными мьютексами усложняет выбор свободного соединения и увеличивает риск ошибок.
Выбранное решение — семафор со стартовым счётчиком восемь для ограничения числа активных операций, а мьютекс использовать отдельно для защиты самой структуры пула. Поток сначала получает разрешение семафора, затем выбирает соединение под мьютексом и после завершения обязательно возвращает разрешение через release.
Такое разделение отвечает двум разным задачам: семафор ограничивает количество участников, а мьютекс защищает данные. Если разрешение не вернуть на каждом пути выхода, включая обработку ошибок, счётчик постепенно исчерпается и новые запросы начнут зависать.
Нет. Семафор хранит только число разрешений и не знает, какой поток выполнил acquire. Поэтому один поток может выполнить acquire, а другой — release. Это полезно для схем producer-consumer, но означает, что семафор сам не обнаружит ошибочное освобождение или передачу не того ресурса.
acquire все предыдущие записи в программе?Нет. Гарантия видимости возникает через конкретную межпоточную связь: успешный acquire должен быть разблокирован соответствующим release, либо данные должны быть опубликованы другим механизмом. Само наличие положительного начального счётчика не превращает произвольные записи в отношениях между потоками в безопасные.
Обычно нет. Семафор может ограничить число потоков, одновременно входящих в участок, но при счётчике больше единицы несколько потоков войдут туда одновременно. Для защиты инвариантов контейнера нужен мьютекс или другая подходящая схема эксклюзивного доступа; семафор можно добавить отдельно, чтобы ограничить общий уровень параллелизма.