Последовательная асинхронная очередь GCD против очереди последовательной синхронизации, вложенной в async

#ios #swift #concurrency #grand-central-dispatch

#iOS #быстрый #параллелизм #гранд-сентрал-диспетчерская

Вопрос:

Я должен защитить критический раздел моего кода. Я не хочу, чтобы вызывающая функция блокировалась функцией, которая может занять много времени, поэтому я создаю последовательную очередь с фоновым 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
         }
    }
}

 

В чем разница между этими двумя подходами?
Какой подход является правильным?

Комментарии:

1. Вторая форма кажется недоразумением для тех, кто не понимает, что локальная очередь отправки может быть выполнена параллельной.

2. Вы никогда не должны вызывать dispatch_sync в параллельной очереди (которой является глобальная очередь), особенно когда продолжительность выполнения «критической секции» может быть большой, поскольку это может привести к «взрыву потока». GCD был разработан так, чтобы приложение создавало как можно меньше потоков. Итак, на самом деле это «анти-шаблон». Итак, разница в том, что подход 1 является жизнеспособным, а подход 2 неправильный.

3. @CouchDeveloper Я согласен с вами, в устаревшей кодовой базе, над которой я работаю, первый подход используется во многих ситуациях, поэтому я сомневался в своем подходе

Ответ №1:

При первом подходе вызывающий поток не блокируется, и задача (критическая секция), переданная в асинхронном блоке, будет выполняться в фоновом режиме.

Во втором подходе вызывающий поток не блокируется, но «фоновый» поток будет ожидать выполнения блока синхронизации (критической секции), который выполняется другим потоком.

Я не знаю, что вы делаете в своем критическом разделе, но, похоже, первый подход кажется лучшим. Обратите внимание, что фоновое qos довольно медленное, возможно, используйте qos по умолчанию для своей очереди, если вы не знаете, что делаете. Также обратите внимание, что соглашение требует, чтобы вы использовали идентификатор пакета в качестве метки для своей очереди. Итак, что-то вроде этого:

 private let someQueue = DispatchQueue(label: "(Bundle.main.bundleIdentifier ?? "").(type(of: self)).someQueue")
 

Комментарии:

1. В моем критическом разделе я записываю журнал в файл, добавляя содержимое. Спасибо за ваше объяснение и предложение qos, вы все еще рекомендуете не использовать background?

2. Как правило, вы должны использовать самый низкий приоритет .utility . Фоновый приоритет может отсутствовать, когда устройство находится в состоянии низкого энергопотребления.