iPhone - главная тема Grand Central Dispatch

145

Я успешно использовал грандиозную центральную диспетчеризацию в своих приложениях, но мне было интересно, каково реальное преимущество использования чего-то подобного:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

или даже

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

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

[self doStuff];

право?

Интересно, что вы, ребята, думаете.

Утка
источник
9
Кстати, добавление основной очереди в dispatch_sync приведет к тупику.
Брукс Хейнс
5
Просто прочитайте это в документации: «В отличие от dispatch_async, [dispatch_sync] не возвращается, пока блок не завершится. Вызов этой функции и ориентация на текущую очередь приводят к тупику.» ... Но, возможно, я читаю это неправильно ... ( текущая очередь не означает основной поток). Пожалуйста, исправьте, если я ошибаюсь.
Брукс Хейнс
4
@BrooksHanes не всегда верно. Это приведет к тупику, если вы уже находитесь в главном потоке. Если нет, то не было бы тупика. Смотрите здесь
Мед

Ответы:

296

Отправка блока в основную очередь обычно выполняется из фоновой очереди, чтобы показать, что некоторая фоновая обработка завершена, например

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

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

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

  • фоновая обработка завершена -> обновить пользовательский интерфейс
  • кусок данных, обрабатываемых в фоновой очереди -> сигнализировать основной очереди, чтобы начать следующий блок
  • входящие данные сети в фоновой очереди -> сигнализируют основной очереди, что сообщение пришло
  • и т. д.

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

Робин Саммерхилл
источник
Ах я вижу. Итак, я прав. В этом нет никакого преимущества, если вы уже находитесь в основной очереди, просто если вы находитесь в другой очереди и хотите обновить пользовательский интерфейс. Спасибо.
Дак
Просто отредактировал мой ответ, чтобы поговорить о том, почему это не очень полезно делать из основной очереди.
Робин Саммерхилл
Кроме того, я думаю, что есть ошибка в iOS 4 (возможно, исчезла в iOS 5), когда dispatch_sync в главную очередь из основного потока просто вызывает зависание, поэтому я бы избегал делать это полностью.
Джорик
10
Это не ошибка, это ожидаемое поведение. Конечно, это не очень полезное поведение, но вам всегда нужно знать о взаимоблокировках при использовании dispatch_sync. Вы не можете ожидать, что система будет постоянно защищать вас от ошибок программиста.
Робин Саммерхилл
2
Что такое backgroundQueue здесь? Как мне создать объект backgroundQueue
Nilesh Tupe
16

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

Например, вы можете написать по существу однопоточный сервер, который, тем не менее, обрабатывает много одновременных соединений. Пока ни один отдельный блок в очереди не займет слишком много времени, сервер будет реагировать на новые запросы.

Если ваша программа ничего не делает, а тратит всю свою жизнь на реагирование на события, это может быть вполне естественно. Вы просто настраиваете свои обработчики событий для запуска в главной очереди, а затем вызываете dispatch_main (), и вам, возможно, вообще не нужно беспокоиться о безопасности потоков.

bames53
источник
11

Надеюсь, я правильно понимаю ваш вопрос в том, что вас интересует разница между dispatch_async и dispatch_sync?

dispatch_async

отправит блок в очередь асинхронно. Это означает, что он отправит блок в очередь и не будет ждать его возврата, прежде чем продолжить выполнение оставшегося кода в вашем методе.

dispatch_sync

отправит блок в очередь синхронно. Это предотвратит дальнейшее выполнение оставшегося кода в методе до завершения выполнения блока.

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

Удачи

timthetoolman
источник
1
спасибо, но я спрашиваю о преимуществах от отправки чего-либо в основную очередь, находясь в главной очереди.
Дак
9

Одним из мест, где это полезно, является деятельность с пользовательским интерфейсом, например, установка счетчика перед длительной операцией:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

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

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

вернет управление в цикл выполнения, который запланирует обновление пользовательского интерфейса, запустив счетчик, затем получит следующую вещь из очереди отправки, которая является вашей фактической обработкой. Когда ваша обработка завершена, вызывается остановка анимации, и вы возвращаетесь в цикл выполнения, где пользовательский интерфейс обновляется с остановкой.

weaselfloss1
источник
@Jerceratops да, но он позволяет завершить текущий цикл выполнения.
Дэн Розенстарк
3
Да, но это все еще ужасно. Это все еще блокирует пользовательский интерфейс. Я мог бы нажать другую кнопку сразу после этой. Или попробуйте и прокрутите. «(сделать что-то длинное)» не должно происходить в главном потоке, и dispatch_async, чтобы позволить нажатию кнопки «закончить», не является приемлемым решением.
Jerceratops
8

Свифт 3, 4 и 5

Запуск кода в главном потоке

DispatchQueue.main.async {
    // Your code here
}
Найл Киддл
источник
1

Async означает асинхронный, и вы должны использовать это большую часть времени. Вы никогда не должны вызывать синхронизацию в главном потоке, потому что он заблокирует ваш пользовательский интерфейс, пока задача не будет завершена. Вы Вот лучший способ сделать это в Swift:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Он включен в качестве стандартной функции в моем репо, проверьте его: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
источник