Что такое mutex и семафоры
Что проверяют
Понимаете ли вы разницу между исключительным доступом и ограничением количества и знаете ли, чем это делается на iOS сегодня. Следом обычно спрашивают, почему семафор на главном потоке вешает приложение и чем заменить блокировки в Swift Concurrency.
Как отвечать
- мьютекс: пускает в критическую секцию один поток и имеет владельца, отпустить его может только захвативший поток. В iOS это
NSLock,NSRecursiveLockдля повторного захвата тем же потоком,pthread_mutex_t,os_unfair_lockи его безопасная обёрткаOSAllocatedUnfairLock(iOS 16). В Swift 6 появилсяMutexиз модуляSynchronization(iOS 18) с методомwithLock; - семафор: счётчик.
DispatchSemaphore(value: 3)пустит трёх, четвёртый вызовwait()будет ждатьsignal(). Владельца нет,signal()можно вызвать из любого потока. Применение: ограничить число одновременных загрузок или соединений; - главное различие: не «один или несколько», а владение. Мьютекс вроде
os_unfair_lockзнает владельца и поднимает его приоритет, когда его ждёт более приоритетный поток. Семафор не знает, кому помогать, и получает инверсию приоритетов, о которой предупреждает Thread Performance Checker в Xcode.
Альтернативы, которые стоит назвать: последовательная DispatchQueue, конкурентная очередь с флагом .barrier для записи, а в коде на async/await это actor, который сериализует доступ к своему состоянию без ручных блокировок.
Где спотыкаются
Говорят, что бинарный семафор и мьютекс одно и то же. Поведение похоже, но у семафора нет владельца, его может «отпустить» чужой поток, и приоритет он не наследует.
Делают асинхронный вызов синхронным через semaphore.wait() на главном потоке. Если колбэк приходит на main, получается дедлок, если нет, интерфейс замирает до ответа сети.
Вызывают DispatchSemaphore.wait() внутри async-функции или держат NSLock захваченным через await. Потоков в кооперативном пуле Swift Concurrency мало, и заблокированный поток тормозит все задачи, а после await задача может продолжиться на другом потоке, который этот мьютекс не захватывал. Компилятор предупреждает, что такие методы недоступны из асинхронного контекста.