Ответ
Вызов DispatchQueue.main.sync из того же самого (главного) потока вызывает взаимную блокировку (deadlock) из-за принципа работы синхронных операций и очередей.
Механизм deadlock:
syncблокирует текущий поток и ждет, пока переданный блок кода выполнится.- Блок кода ставится в очередь (
DispatchQueue.main) на выполнение. - Если очередь уже занята этим же потоком (главный поток выполняет наш
sync-вызов), она не может начать выполнение нового блока, пока не освободится. - Возникает циклическая зависимость: Поток ждет очередь → Очередь ждет поток.
Пример deadlock:
// Вызов из главного потока:
DispatchQueue.main.sync {
// Этот блок никогда не будет выполнен
updateUI()
}
// Поток заблокирован здесь навсегда
Решение: Всегда используйте DispatchQueue.main.async для планирования работы на главной очереди из самого главного потока.
// Правильно:
DispatchQueue.main.async {
self.updateUI()
}
Исключение: Вызов sync из другого (фонового) потока на главную очередь — безопасен и является стандартным паттерном для синхронизации результатов с UI.
Ответ 18+ 🔞
Да ты посмотри, какая интересная хуйня вырисовывается! Вот сидишь ты на главном потоке, царь-батюшка, и вдруг решаешь: "А вызову-ка я DispatchQueue.main.sync!" И всё, приехали. Завис навечно, как дурак.
А че так-то, спросишь?
- Эта команда
sync— она жадная сука. Она хватает текущий поток в ебучую мертвую хватку и не отпускает, пока не выполнится тот кусок кода, который ты ей всунул. - Код-то твой она аккуратненько ставит в очередь, в самую главную,
DispatchQueue.main. - А очередь эта уже занята! Кем? Да тем самым потоком, который сейчас в истерике держится за
syncи орёт "Выполняйся давай!". - И получается пиздецовая круговая порука: Поток орёт на очередь, мол, выполни мой блок! А очередь в ответ: да я и так тебя обслуживаю, отпусти меня сначала, мудак! И оба ждут друг друга до скончания веков. Classic deadlock, ебать его в сраку.
Вот тебе наглядный пиздец:
// Сидишь ты уже на главном потоке и делаешь это:
DispatchQueue.main.sync {
// Этот код — как мечта идиота. Он никогда не случится.
updateUI()
}
// А поток твой намертво застрял вот тут, навеки вечные.
Что делать, чтобы не быть таким мудаком?
Да всё просто, как три копейки! Хочешь что-то кинуть на главный поток из самого главного потока — используй async. Он не жадный, он вежливый: поставит задачу в очередь и пойдёт дальше по своим делам, не блокируя всех и вся.
// Делай так — и будет тебе счастье:
DispatchQueue.main.async {
self.updateUI()
}
А когда sync тогда нужен, спросит дотошный читатель?
А вот когда ты с какого-нибудь фонового потока, с задворков, приполз с результатами и хочешь синхронно, прямо щас, обновить интерфейс. Тогда — да, DispatchQueue.main.sync с фонового потока — это святое, рабочий паттерн. Но из главного в главный — это путь в никуда, в рот меня чих-пых!