Что происходит с приложением при создании большого количества потоков, которые ничего не делают
Что проверяют
Знаете ли вы, что поток это не бесплатная абстракция, и понимаете ли, почему в iOS советуют работать очередями и задачами, а не создавать потоки руками. Заодно проверяют, слышали ли вы про разрастание пула потоков, потому что именно в таком виде этот вопрос приходит из реального приложения.
Как отвечать
Начните с цены одного потока. Ядро заводит структуру планировщика, резервирует адресное пространство под стек (у вторичного потока он по умолчанию заметно меньше, чем у главного, но всё равно сотни килобайт) и добавляет ещё одного кандидата на переключение контекста. Тысяча спящих потоков процессор почти не грузит, зато занимает память и удлиняет каждое переключение: планировщик обходит их все.
Дальше главное: откуда они берутся. Руками потоки почти никто не плодит, пул разрастается сам, когда на глобальной параллельной очереди кто-то блокируется:
DispatchQueue.global().async {
semaphore.wait() // поток занят и ничего не делает
}Очередь видит, что рабочие потоки заняты, и поднимает новые, пока не упрётся в потолок для своего класса качества обслуживания. После этого задачи просто перестают запускаться, приложение подвисает, а сторожевой механизм системы может его завершить.
Лечится это отказом от блокировок внутри потоков пула: async/await с кооперативным пулом размером примерно с число ядер, OperationQueue с ограничением параллельности, последовательная очередь для доступа к общему состоянию.
Где спотыкаются
Считают бездействующий поток бесплатным, раз он не занимает процессор. Память под стек занята сразу, и на устройстве с жёстким лимитом по памяти заканчивается обычно именно она.
Путают потоки и задачи. Тысяча задач в структурированной конкурентности это не тысяча потоков: приостановленная на await задача не держит поток вообще, а вот semaphore.wait держит.
Лечат разрастание пула созданием новых очередей. Каждая параллельная очередь берёт потоки из того же пула, поэтому помогает не добавление очередей, а замена ожидания в потоке на асинхронную приостановку.