Різниця між 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, правильно?

Відповіді:


409

Так. Використання послідовної черги забезпечує послідовне виконання завдань. Єдина відмінність полягає в тому, що 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: відправте трудомістке завдання (завдання 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

5

Це все пов'язано з основною чергою. Існує 4 перестановки.

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

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

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

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

Вибір паралельної чи послідовної черги залежить від того, чи потрібен вам результат попереднього завдання для наступного. Якщо ви залежаєте від попереднього завдання, прийміть послідовну чергу, займіть одночасну чергу.

І нарешті, це спосіб проникнути назад до головної нитки, коли ми закінчимо наш бізнес:

DispatchQueue.main.async {
     // Do something here
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.