В чём заключается роль initialCount и maximumCount у Semaphore в C#?
Долго пытался понять и так и не понял. В чём разница между initialCount
и maximumCount
? Что за что отвечает? Зачем maximumCount
, если initialCount
- это итак максимальное кол-во потоков выполняемых одновременно? и тому подобные вопросы.
Моё понимае: initialCount
отвечает за кол-во выполняемых потоков одновременно, maximumCount
- не понятно.
Где-то прочитал:
initialCount
отвечает за кол-во вызововWaitOne()
, amaximumCount
- за кол-во вызововRelease()
.
Здесь всё становится ещё непонятнее.
В общем был бы благодарен если бы кто-то полностью объяснил за что отвечают initialCount
и maximumCount
.
Ответы (2 шт):
У семафора есть внутренний счётчик, который показывает текущее количество объектов, которые контролируются семафором (например, количество выполняемых потоков).
Если вы при создании семафора указали initialCount =0
, то семафор находится в занятом состоянии. Если вы сейчас запустите потоки, ждущие семафора, то они блокируются.
Чтобы семафор перешёл в сигнальное состояние, нужно выполнить ReleaseSemaphore
с нужным числом n
. При этом счётчик семафора становится равным n
.
Если ReleaseSemaphore
передано слишком большое число, так что в сумме текущий счётчик + release
превышает MaximumCount
, то текущий счётчик не изменяется
Когда семафор перешёл в сигнальное состояние, и есть потоки, ждущие семафора, то каждый из них (не более n
) успешно возвращается из Wait-функции, при каждом успешном Wait счётчик семафора уменьшается на единицу. Если ждущих объектов n
или более, то счётчик обнуляется, семафор переходит в занятое состояние, лишние потоки продолжают ждать.
При завершении потока из него нужно (обычно) вызвать ReleaseSemaphore(1)
, при этом счётчик увеличивается на единицу, семафор сигнализирует следующему ждущему объекту.
Заметьте, что можно создать семафор с ненулевым initialCount
, тогда он сразу готов к запуску соответствующего количества потоков
Вообще в документации вполне понятно всё написано и даже пример приведён, когда начальный счётчик меньше максимального. В принципе, у них говорящие названия и так уже.
Можно рассмотреть просто пару разных use-case, чтобы было понятно, зачем нужен initialCount
, и почему он может отличаться от maximumCount
.
Случай
initialCount == maximumCount
. Это типовое применение семафора. Вот есть у вас некое "бутылочное горлышко": максимальное число потоков, которые вы хотите запустить, максимально допустимое число одновременных логинов на какой-то сервис, да что угодно. И вы не хотите, чтобы какой-то программы процесс превышал этот максимум. Вы инициализируете семафор, у него оба счётчика равны этому максимуму. На входе он из счётчика вычитает, а выходе обратно накидывает. Суть в том, что когда текущий счётчик семафора доходит до нуля, новые заходы в вашу функцию блокируются в ожидании, когда счётчик станет больше нуля. Таким образом вы ограничиваете максимальное число заходов в вашу функцию и у вас какие-то сущности не плодятся больше некоего максимума.Случай
initialCount < maximumCount
. Тут почти всё тоже самое, но вы хотите иметь ещё больше контроля над процессом и не хотите, чтобы сразу был достигнуто максимальное число ваших процессов. Экстремальный случай - когда у васinitialCount = 0
, в этом случае вообще ни один процесс не запустится, все заблокируются на семафоре. Может вы хотите чего-то подождать, после чего вы можете увеличить счётчик сразу или постепенно на разницу между максимумом и начальным значением и дальше у вас семафор будет действовать как в первом случае. Например, вы хотите плавно увеличить нагрузку и для этого с какой-то задержкой несколько раз увеличиваете счётчик. Получается, что сначала у вас один процесс может стартовать, потом два, и так далее, до максимума. В общем, можно придумать какие-то сценарии, когда это нужно.