Разница между dispatch_async и dispatch_sync в последовательной очереди?

125

Я создал такую ​​последовательную очередь:

    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);

В чем разница между так dispatch_asyncназываемым

 dispatch_async(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_async(_serialQueue, ^{ /* TASK 2 */ });

И так dispatch_syncзвонили в эту последовательную очередь?

 dispatch_sync(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_sync(_serialQueue, ^{ /* TASK 2 */ });

Насколько я понимаю, независимо от того, какой метод отправки используется, TASK 1он будет выполнен и завершен раньше TASK 2, правильно?

JRG-разработчик
источник

Ответы:

410

Да. Использование последовательной очереди обеспечивает последовательное выполнение задач. Единственное отличие состоит в том, что dispatch_syncвозврат только после завершения блока, тогда как dispatch_asyncвозврат после того, как он добавлен в очередь и может не завершиться.

для этого кода

dispatch_async(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_async(_serialQueue, ^{ printf("3"); });
printf("4");

Он может печатать 2413или 2143или , 1234но 1всегда перед3

для этого кода

dispatch_sync(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_sync(_serialQueue, ^{ printf("3"); });
printf("4");

он всегда печатает 1234


Примечание. Первый код не печатается 1324. Потому printf("3")что отправляется после printf("2") выполнения. И задача может быть выполнена только после отправки.


Время выполнения задач ничего не меняет. Этот код всегда печатается12

dispatch_async(_serialQueue, ^{ sleep(1000);printf("1"); });
dispatch_async(_serialQueue, ^{ printf("2"); });

Что может случиться

  • Поток 1: dispatch_async трудоемкая задача (задача 1) в последовательную очередь
  • Поток 2: начать выполнение задачи 1
  • Поток 1: dispatch_async другая задача (задача 2) в последовательную очередь
  • Тема 2: задача 1 завершена. начать выполнение задачи 2
  • Тема 2: задача 2 завершена.

и ты всегда видишь 12

Брайан Чен
источник
7
он также может печатать 2134 и 1243
Маттео Гобби,
у меня вопрос: почему мы не сделали это как обычно? printf("1");printf("2") ;printf("3") ;printf("4")- по сравнению сdispatch_sync
androniennn
@androniennn для второго примера? потому что в то dispatch_sync(_serialQueue, ^{ /*change shared data*/ });же время может работать какой-то другой поток .
Брайан Чен
1
@ asma22 Очень полезно разделять небезопасный для потоков объект между несколькими потоками / очередями отправки. Если вы обращаетесь к объекту только в последовательной очереди, вы знаете, что обращаетесь к нему безопасно.
Брайан Чен
1
Я имею в виду серийную казнь . С точки зрения того, что все задачи выполняются последовательно по отношению к другим задачам в той же очереди. Конечно, это все еще может быть одновременным по отношению к другим очередям. Вся суть GCD в том, что задачи могут отправляться и выполняться одновременно.
Брайан Чен
19

Разница между dispatch_syncи dispatch_asyncпроста.

В обоих ваших примерах TASK 1всегда будет выполняться раньше, TASK 2потому что он был отправлен до него.

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

В этом dispatch_asyncпримере ваш код не будет ждать завершения выполнения. Оба блока будут отправлены (и поставлены в очередь) в очередь, а остальная часть вашего кода продолжит выполнение в этом потоке. Затем в какой-то момент в будущем (в зависимости от того, что еще было отправлено в вашу очередь), Task 1будет выполнено, а затем Task 2будет выполнено.

Дэйв Делонг
источник
2
Я думаю, вы ошиблись в заказе. Первый пример - asyncэто неблокирующая версия
Брайан Чен
Я отредактировал ваш ответ так, как я думаю, вы имели в виду . Если это не так, пожалуйста, измените и поясните.
JRG-Developer
1
Что делать, если вы вызываете dispatch_sync, а затем dispatch_async в той же очереди? (и наоборот)
0xSina
1
В последовательной очереди обе задачи по-прежнему выполняются одна за другой. В первом случае вызывающий абонент ожидает завершения первого блока, но не ждет второго блока. Во втором случае вызывающий абонент не ждет завершения первого блока, а ждет второго блока. Но поскольку очередь выполняет блоки по порядку, вызывающий фактически ожидает завершения обоих.
gnasher729
1
Блок также может выполнять dispatch_async в своей собственной очереди (добавляя дополнительные блоки, которые будут выполнены позже); dispatch_sync в собственной последовательной очереди или основной очереди приведет к взаимной блокировке. В этой ситуации вызывающий будет ждать завершения исходного блока, но не других блоков. Просто помните: dispatch_sync помещает блок в конец очереди, очередь выполняет код до тех пор, пока этот блок не будет завершен, а затем возвращается dispatch_sync. dispatch_async просто добавляет блок в конец очереди.
gnasher729
6

Все это связано с основной очередью. Есть 4 перестановки.

i) Последовательная очередь, асинхронная отправка: здесь задачи будут выполняться одна за другой, но основной поток (влияние на пользовательский интерфейс) не будет ждать возврата

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

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

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

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

И, наконец, это способ вернуться к основному потоку, когда мы закончим наши дела:

DispatchQueue.main.async {
     // Do something here
}
rd_
источник