Что-то плохое случится при построении многоуровневой диспетчеризацииQueue? - PullRequest
0 голосов
/ 09 января 2019

Фрагмент 1

let workerQueue = DispatchQueue(label: "foo")
workderQueue.async {
   #code
   DispatchQueue.main.async {
       #code
      workerQueue.async {
            #code

      }
   }
}

Фрагмент 2

let workerQueue = DispatchQueue(label: "foo")
DispatchQueue.main.async {
   #code
   workerQueue.async {
       #code
      DispatchQueue.main.async {
            #code

      }
   }
}

Можно ли писать код, подобный фрагменту 1 или фрагменту 2? Будет ли заблокирован основной поток?

Ответы [ 3 ]

0 голосов
/ 09 января 2019

Оба ваших фрагмента имеют одинаковую структуру. Эта структура абсолютно нормальная и именно так и происходит переключение потоков.

0 голосов
/ 09 января 2019

Нет, в этих шаблонах нет ничего «плохого» по своей сути.

Как было сказано, типичная картина:

workerQueue.async {
    // do something computationally intensive

    DispatchQueue.main.async {
        // update UI and/or model
    }
}

И это мотивировано тем, что «у меня есть что-то достаточно интенсивное, что я не хочу, чтобы оно работало в основной очереди (и отрицательно сказывалось на UX), но когда я закончу, мне нужно обновить свой пользовательский интерфейс и / или модель. "

Довольно редко у вас также есть дополнительный уровень вложенности, который говорит: «и когда я закончу обновление пользовательского интерфейса, мне нужно отправить что-то еще обратно в мою рабочую очередь», как показано в вашем первом примере. Если у вас есть практический пример этого, возможно, вы можете поделиться им с нами, поскольку могут быть более изящные способы рефакторинга, чтобы избежать «башни» вложенных отправлений, что может немного усложнить выполнение кода.

Во втором примере, когда вы сначала отправляете в основной поток, тоже немного нетипично. (Да, нам иногда приходится это делать, но это происходит не так часто.) Я полагаю, вы предполагаете, что уже находитесь в каком-то другом потоке (хотя и не всегда). Если это тот случай, когда код ожидает, что вы находитесь в определенном потоке, вы можете сделать это предположение явным, например ::

func foo() {
    dispatchPrecondition(condition: .onQueue(workerQueue))

    // do some stuff that should be on the `workerQueue`

    DispatchQueue.main.async {
        // update UI, etc.

        ...
    }
}

В итоге, нет абсолютно никаких проблем с произвольным уровнем вложенности, особенно если вы делаете это с async ... выполнение этого с sync может вызвать проблемы тупиковой ситуации, если вы не будете осторожны. Но из практического «могут ли будущие программисты прочитать этот код и четко определить, каким будет конечное поведение», вы часто захотите ограничить это, чтобы это не слишком запутывало.


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

0 голосов
/ 09 января 2019

Вам нужно только выполнить код в желаемом потоке, если вы находитесь в каком-то другом потоке.

Например, поскольку по умолчанию приложение работает в основном потоке. Но если задача выполняется в фоновом потоке и изменяет элемент пользовательского интерфейса внутри себя, то она должна быть заключена в основной поток.

some_background/other_thread_task {
  DispatchQueue.main.async {
       //UI Update
       self.myTableView.reloadData()
  }
}
...