Unterschied zwischen dispatch_async und dispatch_sync in der seriellen Warteschlange?

125

Ich habe eine serielle Warteschlange wie folgt erstellt:

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

Was ist der Unterschied zwischen so dispatch_asyncgenannt

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

Und dispatch_syncso in dieser seriellen Warteschlange angerufen?

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

Mein Verständnis ist, dass unabhängig davon, welche Versandmethode verwendet TASK 1wird, zuvor ausgeführt und abgeschlossen wird TASK 2, richtig?

JRG-Entwickler
quelle

Antworten:

409

Ja. Durch die Verwendung der seriellen Warteschlange wird die serielle Ausführung von Aufgaben sichergestellt. Der einzige Unterschied besteht darin, dass die dispatch_syncRückkehr erst nach Beendigung des Blocks erfolgt, während die dispatch_asyncRückkehr nach dem Hinzufügen zur Warteschlange erfolgt und möglicherweise nicht beendet wird.

für diesen Code

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

Es kann gedruckt werden 2413oder 2143oder 1234aber 1immer vorher3

für diesen Code

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

es wird immer gedruckt 1234


Hinweis: Der erste Code wird nicht gedruckt 1324. Weil printf("3")versendet wird, nachdem printf("2") ausgeführt wurde. Und eine Aufgabe kann erst nach dem Versand ausgeführt werden.


Die Ausführungszeit der Aufgaben ändert nichts. Dieser Code wird immer gedruckt12

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

Was passieren kann ist

  • Thread 1: dispatch_async eine zeitaufwändige Aufgabe (Aufgabe 1) in die serielle Warteschlange
  • Thread 2: Starten Sie die Ausführung von Aufgabe 1
  • Thread 1: dispatch_async eine andere Aufgabe (Aufgabe 2) in die serielle Warteschlange
  • Thread 2: Aufgabe 1 beendet. Starten Sie die Ausführung von Aufgabe 2
  • Thread 2: Aufgabe 2 beendet.

und du siehst immer 12

Bryan Chen
quelle
7
es kann auch 2134 und 1243 drucken
Matteo Gobbi
Meine Frage ist, warum wir es nicht wie gewohnt gemacht haben. printf("1");printf("2") ;printf("3") ;printf("4")- im Vergleich zudispatch_sync
androniennn
@androniennn für zweites Beispiel? weil möglicherweise ein anderer Thread dispatch_sync(_serialQueue, ^{ /*change shared data*/ });gleichzeitig ausgeführt wird.
Bryan Chen
1
@ asma22 Es ist sehr nützlich, ein nicht threadsicheres Objekt für mehrere Threads / Versandwarteschlangen freizugeben. Wenn Sie nur in einer seriellen Warteschlange auf das Objekt zugreifen, wissen Sie, dass Sie sicher darauf zugreifen.
Bryan Chen
1
Ich meine serielle Ausführung . Unter dem Gesichtspunkt, dass alle Aufgaben seriell in Bezug auf andere Aufgaben in derselben Warteschlange ausgeführt werden. Natürlich kann es immer noch gleichzeitig zu anderen Warteschlangen kommen. Es ist der springende Punkt bei GCD, dass Aufgaben gleichzeitig versendet und ausgeführt werden können.
Bryan Chen
19

Der Unterschied zwischen dispatch_syncund dispatch_asyncist einfach.

TASK 1Wird in beiden Beispielen immer vorher ausgeführt, TASK 2da es zuvor versendet wurde.

Im dispatch_syncBeispiel werden Sie jedoch erst versenden, TASK 2nachdem TASK 1sie versandt und ausgeführt wurden . Dies wird als "Blockieren" bezeichnet . Ihr Code wartet (oder "blockiert"), bis die Aufgabe ausgeführt wird.

In diesem dispatch_asyncBeispiel wartet Ihr Code nicht auf den Abschluss der Ausführung. Beide Blöcke werden an die Warteschlange gesendet (und in die Warteschlange gestellt), und der Rest Ihres Codes wird weiterhin in diesem Thread ausgeführt. Dann wird irgendwann in der Zukunft (abhängig davon, was noch an Ihre Warteschlange gesendet wurde) Task 1ausgeführt und dann Task 2ausgeführt.

Dave DeLong
quelle
2
Ich denke du bekommst eine falsche Bestellung. Das erste Beispiel ist asyncdie nicht blockierende Version
Bryan Chen
Ich habe Ihre Antwort auf das bearbeitet, was Sie meiner Meinung nach gemeint haben . Wenn dies nicht der Fall ist, ändern Sie es bitte und klären Sie es.
JRG-Entwickler
1
Was ist, wenn Sie dispatch_sync und dann dispatch_async in derselben Warteschlange aufrufen? (und umgekehrt)
0xSina
1
In einer seriellen Warteschlange werden beide Aufgaben weiterhin nacheinander ausgeführt. Im ersten Fall wartet der Anrufer auf den Abschluss des ersten Blocks, aber nicht auf den zweiten Block. Im zweiten Fall wartet der Anrufer nicht auf den Abschluss des ersten Blocks, sondern auf den zweiten Block. Da die Warteschlange die Blöcke jedoch der Reihe nach ausführt, wartet der Anrufer effektiv darauf, dass beide beendet werden.
Gnasher729
1
Ein Block kann auch dispatch_async in seiner eigenen Warteschlange ausführen (weitere Blöcke hinzufügen, die später ausgeführt werden). dispatch_sync in der eigenen seriellen Warteschlange oder Hauptwarteschlange würde blockieren. In dieser Situation wartet der Aufrufer auf den Abschluss des ursprünglichen Blocks, nicht jedoch auf die anderen Blöcke. Denken Sie daran: dispatch_sync stellt den Block an das Ende der Warteschlange, die Warteschlange führt Code aus, bis dieser Block fertig ist, und dispatch_sync kehrt zurück. dispatch_async fügt nur den Block am Ende der Warteschlange hinzu.
Gnasher729
5

Es hängt alles mit der Hauptwarteschlange zusammen. Es gibt 4 Permutationen.

i) Serielle Warteschlange, asynchroner Versand: Hier werden die Aufgaben nacheinander ausgeführt, aber der Hauptthread (Auswirkung auf die Benutzeroberfläche) wartet nicht auf die Rückgabe

ii) Serielle Warteschlange, Versandsynchronisierung: Hier werden die Aufgaben nacheinander ausgeführt, aber der Hauptthread (Auswirkung auf die Benutzeroberfläche) zeigt eine Verzögerung

iii) Gleichzeitige Warteschlange, asynchroner Versand: Hier werden die Aufgaben parallel ausgeführt und der Hauptthread (Auswirkung auf die Benutzeroberfläche) wartet nicht auf die Rückgabe und ist reibungslos.

iv) Gleichzeitige Warteschlange, Versandsynchronisierung: Hier werden die Aufgaben parallel ausgeführt, aber der Hauptthread (Auswirkung auf die Benutzeroberfläche) zeigt eine Verzögerung

Ihre Wahl der gleichzeitigen oder seriellen Warteschlange hängt davon ab, ob Sie für die nächste eine Ausgabe einer vorherigen Aufgabe benötigen. Wenn Sie von der vorherigen Aufgabe abhängig sind, übernehmen Sie die serielle Warteschlange, andernfalls nehmen Sie die gleichzeitige Warteschlange.

Und schließlich ist dies eine Möglichkeit, zum Hauptthema zurückzukehren, wenn wir mit unserem Geschäft fertig sind:

DispatchQueue.main.async {
     // Do something here
}
rd_
quelle