Что такое DispatchSemaphore?
Что проверяют
Понимаете ли вы цену блокировки потока. DispatchSemaphore это самый грубый инструмент синхронизации в GCD, и вопрос обычно о том, знаете ли вы, когда он уместен, а когда вы своими руками устраиваете зависание приложения.
Как отвечать
DispatchSemaphore это счётчик разрешений. wait уменьшает счётчик и блокирует поток, если разрешений не осталось, signal увеличивает счётчик и будит ожидающего. Значение 1 даёт взаимное исключение, значение N ограничивает число одновременных операций, например параллельных загрузок.
Второе применение это ожидание асинхронного результата: семафор со значением нуль, signal в колбэке, wait снаружи. Приём законный в консольных утилитах и тестах, но в приложении он превращает асинхронный код в синхронный.
У wait есть вариант с таймаутом, возвращающий результат ожидания. Игнорировать его нельзя: истёкший таймаут иначе выглядит как успешный захват.
let limit = DispatchSemaphore(value: 4)
limit.wait()
defer { limit.signal() }Для простого взаимного исключения чаще берут последовательную очередь или блокировку: они дешевле и читаются лучше.
Где спотыкаются
Ждут на главном потоке. Пока wait держит поток, интерфейс не рисуется и не отвечает на касания, а если разрешение должен вернуть колбэк, назначенный на главную очередь, приложение встаёт намертво.
Смешивают семафор с async/await. Пул кооперативных потоков рассчитан на то, что задача не блокируется, и блокирующее ожидание внутри задачи выбивает поток из пула вплоть до полной остановки. Ждать там нужно средствами самой модели задач.
Теряют signal на ранних выходах и ошибках. Разрешение обязан вернуть каждый путь исполнения, поэтому освобождение сразу пишут через defer. Уничтожение семафора, счётчик которого ниже начального, роняет процесс.