Мне нужно защитить критическую часть моего кода. Я не хочу, чтобы вызывающая сторона была заблокирована функцией, которая может занимать много времени, поэтому я создаю последовательную очередь с фоновым qos, а затем асинхронно отправляю:
private let someQueue = DispatchQueue(label: "\(type(of: self)).someQueue", qos: .background)
func doSomething() {
self.someQueue.async {
//critical section
}
}
Насколько я понимаю, функция будет напрямую возвращаться в вызывающий поток без блокировки. Я также видел где-то асинхронную отправку сначала в глобальную очередь, синхронно в последовательную очередь:
private let someQueue2 = DispatchQueue(label: "\(type(of: self)).someQueue2")
func doSomething() {
DispatchQueue.global(qos: .background).async {
self.someQueue2.sync {
//critical section
}
}
}
В чем разница между двумя подходами? Какой правильный подход?
Вы никогда не должны вызывать dispatch_sync в параллельной очереди (которой является глобальная очередь), особенно когда продолжительность выполнения «критического раздела» может быть большой, поскольку это может привести к «взрыву потока». GCD был разработан таким образом, чтобы приложение создавало как можно меньше потоков. Итак, это на самом деле «антипаттерн». Итак, разница в том, что подход 1 является жизнеспособным, а подход 2 неправильным.
@CouchDeveloper Я согласен с вами, в устаревшей кодовой базе, над которой я работаю, первый подход используется во многих ситуациях, поэтому я сомневался в своем подходе.
При первом подходе вызывающий поток не блокируется, и задача (критическая секция), переданная в асинхронном блоке, будет выполняться в фоновом режиме.
Во втором подходе вызывающий поток не блокируется, но «фоновый» поток будет ожидать выполнения блока синхронизации (критического раздела), который выполняется другим потоком.
Я не знаю, что вы делаете в своем критическом разделе, но кажется, что первый подход кажется лучшим. Обратите внимание, что фоновое qos довольно медленное, возможно, используйте qos по умолчанию для своей очереди, если вы не знаете, что делаете. Также обратите внимание, что соглашение требует, чтобы вы использовали идентификатор пакета в качестве метки для своей очереди. Что-то вроде этого:
private let someQueue = DispatchQueue(label: "\(Bundle.main.bundleIdentifier ?? "").\(type(of: self)).someQueue")
В моем критическом разделе я пишу журнал в файле, добавляя содержимое. Спасибо за ваше объяснение и предложение QOS, вы по-прежнему рекомендуете не использовать фон?
Как правило, самый низкий приоритет, который вы должны использовать, — это .utility
. Фоновый приоритет может быть отключен, когда устройство находится в состоянии низкого энергопотребления.
Вторая форма кажется недоразумением того, кто не понимает, что локальная очередь отправки может быть сделана параллельной.