В чем разница между NSLock и NSRecursiveLock для синхронизации в iOS?

«В чем разница между NSLock и NSRecursiveLock для синхронизации в iOS?» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

NSLock — это нерекурсивная блокировка (mutex). Попытка потока, который уже владеет этой блокировкой, захватить её снова приведёт к взаимной блокировке (deadlock) этого потока.

NSRecursiveLock — это рекурсивная блокировка. Она позволяет одному и тому же потоку захватывать её многократно, что необходимо для рекурсивных функций или цепочек методов, использующих общий ресурс.

Практическое правило: Используйте NSRecursiveLock, когда код в защищаемой критической секции может (прямо или косвенно) снова вызвать код, который также пытается захватить ту же самую блокировку.

Пример сценария для NSRecursiveLock:

let recursiveLock = NSRecursiveLock()
var sharedResource = 0

func methodA() {
    recursiveLock.lock()
    defer { recursiveLock.unlock() }
    sharedResource += 1
    methodB() // Вызов другого метода, которому тоже нужен этот lock
}

func methodB() {
    recursiveLock.lock() // Без NSRecursiveLock здесь был бы deadlock!
    defer { recursiveLock.unlock() }
    sharedResource *= 2
}

// Вызов
methodA() // Работает корректно с NSRecursiveLock

Сравнение производительности: NSLock обычно легче и немного быстрее, чем NSRecursiveLock, так как ему не нужно отслеживать глубину рекурсии. Поэтому для нерекурсивного кода всегда предпочтительнее NSLock.

Альтернатива в Swift: Рассмотрите использование os_unfair_lock (более производительный, но нерекурсивный) или рекурсивных мьютексов из pthread для сложных сценариев.