Конечно, вы можете использовать его, но он будет срабатывать только в том случае, если все семафоры запущены одновременно. В зависимости от того, как структурирована остальная часть вашего приложения, действительно могут быть проблемы с голоданием. Например, если у вас есть два ресурса, A и B, и три потока:
- Постоянно берите ресурс A, работайте с ним в течение одной секунды, затем отпустите его и выполните цикл
- Постоянно берите ресурс B, работайте с ним в течение одной секунды, затем отпустите его и выполните цикл
- Подождите, пока будут доступны и A, и B
Вы можете легко подождать, пока и A, и B будут доступны одновременно.
В зависимости от вашего приложения, может быть, лучше просто расположить каждый семафор по порядку, что позволяет избежать этой проблемы с голоданием, но создает традиционные проблемы взаимоблокировки. Однако, если вы уверены, что эти блокировки будут доступны большую часть времени, это может быть безопасно (но также может быть бомба замедленного действия, просто ожидающая, пока ваше приложение будет под реальной нагрузкой ...)
Учитывая ваш пример кода, другим вариантом будет создание глобального упорядочения по семафорам - скажем, упорядочение по имени - и всегда убедитесь, что вы приобретаете их в этом порядке. Если вы сделаете это, вы можете выполнить мультиблокировку, просто заблокировав каждый семафор один за другим в порядке возрастания.
В этом случае порядок освобождения не имеет особого значения, но если вы отпускаете не по порядку, вы должны снять все блокировки «после» блокировки, которую вы только что сняли, прежде чем приобретать больше (это практическое правило, которое должен обеспечить вам тупиковую безопасность. Возможно, это удастся еще больше ослабить с помощью подробного анализа). Рекомендуемый способ заключается в том, чтобы просто выпустить в обратном порядке приобретения, где это возможно, и в этом случае вы можете использовать его для дальнейшего приобретения в любой момент. Например:
- Приобрести замок A
- Приобрести замок B
- Получить замок C
- Снять замок C
- Приобрести замок D
- Выпуск B (теперь не приобретайте ничего, пока не отпустите D!)
- Выпуск D
- Приобрести E
- Выпуск E
- Выпуск A
Пока все следует этим правилам, тупик не должен быть возможным, поскольку циклы официантов не могут образовываться.
Недостатком этого подхода является то, что он может задерживать другие потоки, удерживая блокировку в ожидании другого. Это не будет длиться вечно; в приведенном выше примере трех потоков мы можем иметь такой сценарий, например:
- При запуске поток 2 содержит B. Поток 1 содержит A.
- Резьба 3 блока на A.
- (проходит время)
- Тема 1 выпускает A.
- Резьба 3, замки А, блоки на Б.
- Резьба 1 блоков на A.
- (проходит время)
- Тема 2 выпускает Б.
- Thread 3 блокирует B, работает, затем разблокирует.
- Поток 1 блокирует A, делает успехи.
Как вы можете видеть, было некоторое время простоя, когда поток 1 был заблокирован на A, хотя не было никакой реальной работы, которую нужно было сделать. Тем не менее, благодаря этому мы значительно повысили шансы потока 3 на успехи.
Будет ли это хороший компромисс, будет зависеть от вашего приложения - если вы можете однозначно сказать, что несколько потоков никогда не входят в блокировку, это может даже не иметь значения. Но нет единственно верного пути:)