Встречается на 63% собеседований по Mobile

Какие есть известные проблемы с многопоточностью

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

Состояние гонки (Race Condition)

Состояние гонки возникает, когда два или более потока одновременно обращаются к общим данным без синхронизации, и результат зависит от порядка выполнения потоков. Это приводит к непредсказуемому поведению и ошибкам, которые сложно воспроизвести.

Пример на Swift:

swift
var sharedResource = [String]()

DispatchQueue.global().async {
    for _ in 0..<1000 {
        sharedResource.append("A")
    }
}

DispatchQueue.global().async {
    for _ in 0..<1000 {
        sharedResource.append("B")
    }
}
// Итоговое содержимое массива непредсказуемо: возможны потеря данных или сбой.

Для предотвращения состояния гонки используют блокировки, семафоры, очереди или атомарные операции.

Взаимная блокировка (Deadlock)

Взаимная блокировка происходит, когда потоки блокируются, ожидая ресурсы, которые удерживают друг друга, и ни один не может продолжить выполнение. Это приводит к зависанию приложения.

Пример на Swift:

swift
let queue1 = DispatchQueue(label: "queue1")
let queue2 = DispatchQueue(label: "queue2")

queue1.async {
    queue2.sync {
        // Делаем что-то
    }
}

queue2.async {
    queue1.sync {
        // Делаем что-то
    }
}
// Потоки ждут друг друга бесконечно.

Чтобы избежать взаимных блокировок, следует соблюдать порядок захвата ресурсов, использовать таймауты или избегать вложенных блокировок.

Гонки памяти (Memory Races)

Гонки памяти — это разновидность состояния гонки, при которой конфликт возникает на уровне доступа к памяти: несколько потоков одновременно читают и записывают данные в одну область памяти без синхронизации. Это может привести к повреждению данных и неопределённому поведению. В Swift компилятор может выявлять такие проблемы с помощью Thread Sanitizer.

Голодание (Starvation)

Голодание возникает, когда поток не получает доступа к ресурсу, потому что другие потоки постоянно его захватывают. Например, поток с низким приоритетом может никогда не выполниться, если высокоприоритетные потоки постоянно занимают ресурс. Это снижает отзывчивость приложения.

Чрезмерная синхронизация (Over-Synchronization)

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

Коротко

  • Состояние гонки — несинхронизированный доступ к общим данным, результат непредсказуем.
  • Взаимная блокировка — потоки ждут друг друга, приложение зависает.
  • Гонки памяти — повреждение данных из-за одновременного доступа к памяти.
  • Голодание — поток не получает ресурс из-за постоянного захвата другими.
  • Чрезмерная синхронизация — избыток блокировок снижает производительность.

Для борьбы с этими проблемами используйте GCD, NSOperation, блокировки, семафоры и атомарные свойства, а также инструменты вроде Thread Sanitizer.

как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы