В чём заключается роль initialCount и maximumCount у Semaphore в C#?

Долго пытался понять и так и не понял. В чём разница между initialCount и maximumCount? Что за что отвечает? Зачем maximumCount, если initialCount - это итак максимальное кол-во потоков выполняемых одновременно? и тому подобные вопросы.

Моё понимае: initialCount отвечает за кол-во выполняемых потоков одновременно, maximumCount - не понятно.

Где-то прочитал:

initialCount отвечает за кол-во вызовов WaitOne(), a maximumCount - за кол-во вызовов Release().

Здесь всё становится ещё непонятнее.

В общем был бы благодарен если бы кто-то полностью объяснил за что отвечают initialCount и maximumCount.


Ответы (2 шт):

Автор решения: MBo

У семафора есть внутренний счётчик, который показывает текущее количество объектов, которые контролируются семафором (например, количество выполняемых потоков).

Если вы при создании семафора указали initialCount =0, то семафор находится в занятом состоянии. Если вы сейчас запустите потоки, ждущие семафора, то они блокируются.

Чтобы семафор перешёл в сигнальное состояние, нужно выполнить ReleaseSemaphore с нужным числом n. При этом счётчик семафора становится равным n.

Если ReleaseSemaphore передано слишком большое число, так что в сумме текущий счётчик + release превышает MaximumCount, то текущий счётчик не изменяется

Когда семафор перешёл в сигнальное состояние, и есть потоки, ждущие семафора, то каждый из них (не более n) успешно возвращается из Wait-функции, при каждом успешном Wait счётчик семафора уменьшается на единицу. Если ждущих объектов n или более, то счётчик обнуляется, семафор переходит в занятое состояние, лишние потоки продолжают ждать.

При завершении потока из него нужно (обычно) вызвать ReleaseSemaphore(1), при этом счётчик увеличивается на единицу, семафор сигнализирует следующему ждущему объекту.

Заметьте, что можно создать семафор с ненулевым initialCount, тогда он сразу готов к запуску соответствующего количества потоков

→ Ссылка
Автор решения: CrazyElf

Вообще в документации вполне понятно всё написано и даже пример приведён, когда начальный счётчик меньше максимального. В принципе, у них говорящие названия и так уже.

Можно рассмотреть просто пару разных use-case, чтобы было понятно, зачем нужен initialCount, и почему он может отличаться от maximumCount.

  1. Случай initialCount == maximumCount. Это типовое применение семафора. Вот есть у вас некое "бутылочное горлышко": максимальное число потоков, которые вы хотите запустить, максимально допустимое число одновременных логинов на какой-то сервис, да что угодно. И вы не хотите, чтобы какой-то программы процесс превышал этот максимум. Вы инициализируете семафор, у него оба счётчика равны этому максимуму. На входе он из счётчика вычитает, а выходе обратно накидывает. Суть в том, что когда текущий счётчик семафора доходит до нуля, новые заходы в вашу функцию блокируются в ожидании, когда счётчик станет больше нуля. Таким образом вы ограничиваете максимальное число заходов в вашу функцию и у вас какие-то сущности не плодятся больше некоего максимума.

  2. Случай initialCount < maximumCount. Тут почти всё тоже самое, но вы хотите иметь ещё больше контроля над процессом и не хотите, чтобы сразу был достигнуто максимальное число ваших процессов. Экстремальный случай - когда у вас initialCount = 0, в этом случае вообще ни один процесс не запустится, все заблокируются на семафоре. Может вы хотите чего-то подождать, после чего вы можете увеличить счётчик сразу или постепенно на разницу между максимумом и начальным значением и дальше у вас семафор будет действовать как в первом случае. Например, вы хотите плавно увеличить нагрузку и для этого с какой-то задержкой несколько раз увеличиваете счётчик. Получается, что сначала у вас один процесс может стартовать, потом два, и так далее, до максимума. В общем, можно придумать какие-то сценарии, когда это нужно.

→ Ссылка