Ответ
Нет, не всегда. Deadlock гарантированно произойдет только если sync вызывается на DispatchQueue.main из того же самого MainThread.
Механизм deadlock:
syncблокирует текущий поток до завершения переданного блока.- Если этот вызов делается из MainThread, поток блокируется и ждет.
- Поскольку
MainQueueпоследовательная, она не может начать выполнение нового блока (того, который мы передали вsync), пока не завершит текущий (тот, из которого был вызванsync). Возникает взаимная блокировка.
Пример deadlock:
// ВЫЗОВ ИЗ MAIN THREAD -> DEADLOCK
DispatchQueue.main.sync {
// Этот блок никогда не начнет выполняться
print("This will never print")
}
Безопасный вызов: Если sync на DispatchQueue.main вызывается из другого потока (например, из background очереди), deadlock не произойдет. Background поток заблокируется, main поток выполнит блок и разблокирует background поток.
Пример безопасного вызова:
DispatchQueue.global().async {
// Вызов из фонового потока — БЕЗОПАСНО
DispatchQueue.main.sync {
// Обновляем UI на MainThread
self.label.text = "Updated safely"
}
// Код продолжит выполнение здесь после завершения блока выше
}
Правило: Никогда не вызывайте DispatchQueue.main.sync из кода, который может выполняться на MainThread.
Ответ 18+ 🔞
А, слушай, вот эта тема с дедлоком на главной очереди — это классика, прям как в анекдоте про двух баранов на мосту, блядь. Все думают, что sync на main — это всегда мгновенный пиздец, но не всё так однозначно, ёпта!
Вот смотри, в чём тут собака зарыта, а точнее, где она сдохнет. Дедлок — это гарантированный подарок судьбы только в одном, ебучем случае: когда ты вызываешь DispatchQueue.main.sync прямо с самой главной очереди, с MainThread'а.
Как это работает, или механизм самоубийства:
- Команда
syncговорит: «Эй, поток, стой тут, не дыши, пока я не сделаю то, что в этих фигурных скобках». - Если ты уже на MainThread, то этот поток и есть тот самый единственный мудак, который должен всё делать. Он встаёт как вкопанный и ждёт.
- А очередь
main— она последовательная, сука. Она не может начать выполнять новый блок (тот, что ты вsyncзасунул), пока не закончит старый (тот, из которого ты этотsyncи вызвал). Получается патовая ситуация: один ждёт другого, а они, блядь, один и тот же! В рот меня чих-пых, вот и дедлок.
Пример, как НЕ НАДО делать (классика жанра):
// Делаем это прямо из MAIN THREAD -> ПИЗДЕЦ, ТУПИК
DispatchQueue.main.sync {
// Этот код — как мечта идиота. Он никогда не исполнится.
print("А вот и нет, сука")
}
А теперь безопасный способ, когда всё ок:
Если ты позовёшь главную очередь синхронно из другого потока (ну, с какой-нибудь фоновой, global()), то никакого дедлока не будет. Фоновый поток просто присядет на корточки и подождёт, пока главный сделает свои дела.
Пример, как можно (и иногда нужно):
DispatchQueue.global().async {
// Мы тут на фоне, в безопасности
// Зовём главную очередь синхронно — МОЖНО!
DispatchQueue.main.sync {
// Тут обновляем интерфейс, и всё чётко.
self.button.title = "Всё ок, ёпта"
}
// А после того как главный поток отработал, мы тут и продолжим.
}
Итоговое правило, которое надо выжечь на подкорке: Никогда, блядь, НИКОГДА не вызывай DispatchQueue.main.sync из кода, который может выполняться на главном потоке. А то будет как с Герасимом и Муму — сам утопишь, а потом метаться будешь.