Какое минимальное количество потоков необходимо для возникновения взаимной блокировки (deadlock)?

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

Ответ

Для возникновения deadlock достаточно двух потоков.

Условия взаимной блокировки (Coffman conditions):

  1. Взаимное исключение: Потоки удерживают монопольный доступ к ресурсам (например, через NSLock).
  2. Удержание и ожидание: Поток, удерживая один ресурс, пытается получить доступ к другому.
  3. Отсутствие вытеснения: Ресурс нельзя отнять у потока, только добровольно освободить.
  4. Циклическое ожидание: Потоки образуют кольцо, где каждый ждет ресурс, удерживаемый следующим.

Пример deadlock на Swift с двумя потоками:

let lockA = NSLock()
let lockB = NSLock()

DispatchQueue.global().async {
    lockA.lock()
    sleep(1) // Имитация работы с ресурсом A
    // Поток 1 ждет, пока освободится lockB
    lockB.lock() // БЛОКИРОВКА: lockB удерживается потоком 2
    // Критическая секция...
    lockB.unlock()
    lockA.unlock()
}

DispatchQueue.global().async {
    lockB.lock()
    sleep(1) // Имитация работы с ресурсом B
    // Поток 2 ждет, пока освободится lockA
    lockA.lock() // БЛОКИРОВКА: lockA удерживается потоком 1
    // Критическая секция...
    lockA.unlock()
    lockB.unlock()
}
// Оба потока заблокированы навсегда.

Как избежать: устанавливать строгий порядок захвата блокировок или использовать методы вроде NSLock.try().