Unterschied zwischen DispatchQueue.main.async und DispatchQueue.main.sync

97

Ich habe DispatchQueue.main.asyncfür eine lange Zeit verwendet, um UI-bezogene Operationen durchzuführen.



Swift stellt beide DispatchQueue.main.asyncund zur Verfügung DispatchQueue.main.sync, und beide werden in der Hauptwarteschlange ausgeführt.



Kann mir jemand den Unterschied zwischen ihnen sagen? Wann sollte ich jeden verwenden?



DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}
Aman.Samghani
quelle

Antworten:

40

Wenn Sie es verwenden async, wird die aufrufende Warteschlange fortgesetzt, ohne zu warten, bis der versendete Block ausgeführt wird. Im Gegenteil sync, die anrufende Warteschlange wird angehalten und gewartet, bis die Arbeit, die Sie im Block gesendet haben, erledigt ist. Daher kann synces zu Deadlocks kommen. Wenn Sie versuchen, DispatchQueue.main.syncvon der Hauptwarteschlange aus zu laufen, friert die App ein, da die aufrufende Warteschlange wartet, bis der versendete Block beendet ist, aber nicht einmal gestartet werden kann (weil die Warteschlange gestoppt ist und wartet).

Wann verwenden sync? Wenn Sie auf etwas warten müssen, das in einer VERSCHIEDENEN Warteschlange erledigt wurde, und erst dann weiter an Ihrer aktuellen Warteschlange arbeiten müssen

Beispiel für die Verwendung von Sync:

In einer seriellen Warteschlange können Sie syncals Mutex verwenden, um sicherzustellen, dass nur ein Thread gleichzeitig den geschützten Code ausführen kann.

Andrey Chernukha
quelle
Wäre es falsch, DispatchQueue.main.syncvon einem Hintergrund-Thread aus aufzurufen ?
Honig
@Honey Im Allgemeinen nein, an einem solchen Anruf ist nichts auszusetzen (solange die Hauptwarteschlange nichts Schweres und Zeitraubendes tut), aber in der Praxis kann ich mir keine Situation vorstellen, in der Sie dies wirklich brauchen. Es sollte definitiv eine bessere Lösung geben
Andrey Chernukha
1
@Honey Eine solche Situation ist das Aktualisieren einer CollectionView von PHAssets von der PhotoKit-API, wie in der Dokumentation hier gezeigt: developer.apple.com/documentation/photokit/…
teacup
@Teacup interessant. Ich frage mich nur, wie es anders wäre, wenn wir asyncdort anrufen würden. Ich meine, da es danach nichts mehr auf dem Thread gibt, macht es keinen Unterschied. Wenn es DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};so wäre, hätte es Sinn gemacht. Aber wenn es keinen anderen Block gibt, kann ich mir den Vorteil der Verwendung von DispatchQueue.main.sync {Oneblock}over nicht vorstellen DispatchQueue.main.async {Oneblock}. Für beide erhalten sie die mainQueue-Priorität / Unmittelbarkeit und nichts würde sie unterbrechen.
Honig
2
@Honey "da sich danach nichts mehr im Thread befindet" ist nicht wahr, wenn Sie sich im Haupt-Thread befinden, der für alle Benutzerinteraktionen mit der App verantwortlich ist. So kann ein Benutzer beispielsweise ein anderes Foto löschen, bevor photoLibraryDidChange mit einer aktualisierten Datenquelle zurückkehrt, was zu einem schwerwiegenden Inkonsistenzfehler führt.
Teetasse
159

Warum Parallelität?

Sobald Sie Ihrer App schwere Aufgaben wie das Laden von Daten hinzufügen, verlangsamt dies Ihre UI-Arbeit oder friert sie sogar ein. Mit Parallelität können Sie zwei oder mehr Aufgaben „gleichzeitig“ ausführen. Der Nachteil dieses Ansatzes ist die Gewindesicherheit, die nicht immer so einfach zu kontrollieren ist. Zum Beispiel, wenn verschiedene Aufgaben auf dieselben Ressourcen zugreifen möchten, z. B. der Versuch, dieselbe Variable in einem anderen Thread zu ändern oder auf die Ressourcen zuzugreifen, die bereits von den verschiedenen Threads blockiert wurden.

Es gibt einige Abstraktionen, die wir beachten müssen.

  • Warteschlangen.
  • Leistung synchroner / asynchroner Aufgaben.
  • Prioritäten.
  • Häufige Probleme.

Warteschlangen

Muss seriell oder gleichzeitig sein . Sowie global oder privat zugleich.

Bei seriellen Warteschlangen werden Aufgaben einzeln erledigt, während bei gleichzeitigen Warteschlangen Aufgaben gleichzeitig ausgeführt und nach unerwarteten Zeitplänen ausgeführt werden. Dieselbe Gruppe von Aufgaben benötigt in einer seriellen Warteschlange mehr Zeit als in einer gleichzeitigen Warteschlange.

Sie können Ihre eigenen privaten Warteschlangen (sowohl seriell als auch gleichzeitig ) erstellen oder bereits verfügbare globale (System-) Warteschlangen verwenden . Die Hauptwarteschlange ist die einzige serielle Warteschlange aller globalen Warteschlangen .

Es wird dringend empfohlen, keine schweren Aufgaben auszuführen, die nicht als UI-Arbeit in der Hauptwarteschlange bezeichnet werden (z. B. Laden von Daten aus dem Netzwerk), sondern in den anderen Warteschlangen, um die Benutzeroberfläche nicht eingefroren zu halten und auf die Benutzeraktionen zu reagieren. Wenn wir die Benutzeroberfläche in den anderen Warteschlangen ändern lassen, können die Änderungen nach einem anderen und unerwarteten Zeitplan und einer anderen Geschwindigkeit vorgenommen werden. Einige UI-Elemente können vor oder nach ihrer Verwendung gezeichnet werden. Es kann die Benutzeroberfläche zum Absturz bringen. Da es sich bei den globalen Warteschlangen um Systemwarteschlangen handelt, müssen einige andere Aufgaben vom System ausgeführt werden.

Servicequalität / Priorität

Warteschlangen haben auch unterschiedliche QOS (Quality of Service), die die Priorität für die Ausführung der Aufgabe festlegen ( hier von der höchsten zur niedrigsten):
.userInteractive - Hauptwarteschlange
.userInitiated - für vom Benutzer initiierte Aufgaben, bei denen der Benutzer auf eine Antwort wartet
.utility - für die Aufgaben
Dies dauert einige Zeit und erfordert keine sofortige Reaktion, z. B. Arbeiten mit Daten. Hintergrund - für Aufgaben, die nicht mit dem visuellen Teil zusammenhängen und für die Fertigstellungszeit nicht streng sind.

Es gibt auch eine

Standardwarteschlange, die die QOS- Informationen nicht überträgt . Wenn es nicht möglich war, die QOS zu erkennen dieqos wird zwischen .userInitiated und .utility verwendet .

Aufgaben können synchron oder asynchron ausgeführt werden .

  • Die synchrone Funktion gibt die Steuerung erst nach Abschluss der Aufgabe an die aktuelle Warteschlange zurück. Es blockiert die Warteschlange und wartet, bis die Aufgabe beendet ist.

  • Die asynchrone Funktion gibt die Steuerung direkt nach dem Senden der Aufgabe an die aktuelle Warteschlange zurück, um sie in der anderen Warteschlange auszuführen. Es wartet nicht, bis die Aufgabe beendet ist. Die Warteschlange wird nicht blockiert.

Häufige Probleme.

Die beliebtesten Fehler, die Programmierer beim Projizieren der gleichzeitigen Apps machen, sind folgende:

  • Race Condition - Wird verursacht, wenn die App funktioniert. Dies hängt von der Reihenfolge der Ausführung der Codeteile ab.
  • Prioritätsumkehrung - Wenn die Aufgaben mit höherer Priorität darauf warten, dass die Aufgaben mit kleinerer Priorität abgeschlossen werden, weil einige Ressourcen blockiert sind
  • Deadlock - Wenn einige Warteschlangen unendlich lange auf die Quellen (Variablen, Daten usw.) warten, die bereits von einigen dieser Warteschlangen blockiert wurden.

Rufen Sie NIEMALS die Synchronisierungsfunktion in der Hauptwarteschlange auf .
Wenn Sie die Synchronisierungsfunktion in der Hauptwarteschlange aufrufen, wird die Warteschlange blockiert, und die Warteschlange wartet auf den Abschluss der Aufgabe. Die Aufgabe wird jedoch nie beendet, da sie aufgrund der Warteschlange nicht einmal gestartet werden kann bereits gesperrt. Es heißt Deadlock .

Wann soll die Synchronisierung verwendet werden? Wenn wir warten müssen, bis die Aufgabe erledigt ist. Fe, wenn wir sicherstellen, dass eine Funktion / Methode nicht doppelt aufgerufen wird. Fe wir haben Synchronisation und versuchen zu verhindern, dass es doppelt aufgerufen wird, bis es vollständig fertig ist. Hier ist ein Code für dieses Problem:
Wie kann man herausfinden, was einen Fehler-Absturzbericht auf dem IOS-Gerät verursacht hat?

Alexander
quelle
3
Ich denke nicht, dass "NIEMALS die Synchronisierungsfunktion in der Hauptwarteschlange aufrufen" richtig ist. Es gibt Fälle, in denen Sie die Synchronisierung im Hauptthread aufrufen, z. B. wenn Sie einen globalen Zähler haben, für den jedes Objekt verwendet werden muss, und Folgendes erhöhen: dispatchQueue.sync {count + = 1; self.orderId = count}
Elisha Sterngold
6
QOS-Klasse - .userInteractive ist NICHT die Hauptwarteschlange.
Kunal Shah
1
Wäre es falsch, DispatchQueue.main.syncvon einem Hintergrund-Thread aus aufzurufen ?
Schatz
1
@Honey, nein, das ist nicht falsch, aber meiner Erfahrung nach würden Sie mehr von DispatchQueue.main.async als sync aufrufen.
James Kim
2
Wäre es nicht genauer zu sagen, dass Sie niemals die Funktion sync () in der aktuellen Warteschlange aufrufen sollten? Es ist nicht falsch, sync () in der Hauptwarteschlange aufzurufen, wenn Sie sich in einer anderen Warteschlange befinden, wenn ich das richtig verstehe.
Ykay
0

syncoder asyncMethoden haben keine Auswirkung auf die Warteschlange, in der sie aufgerufen werden.

syncblockiert den Thread, von dem aus er aufgerufen wird, und nicht die Warteschlange, in der er aufgerufen wird. Es ist die Eigenschaft, DispatchQueuedie entscheidet, ob der DispatchQueueBenutzer auf die Ausführung der Aufgabe wartet (serielle Warteschlange) oder die nächste Aufgabe ausführen kann, bevor die aktuelle Aufgabe abgeschlossen ist (gleichzeitige Warteschlange).

Selbst wenn DispatchQueue.main.asynces sich um einen asynchronen Aufruf handelt, kann eine hinzugefügte Hochleistungsoperation die Benutzeroberfläche einfrieren, da ihre Operationen seriell auf dem Hauptthread ausgeführt werden. Wenn diese Methode vom Hintergrund-Thread aufgerufen wird, kehrt die Steuerung sofort zu diesem Thread zurück, selbst wenn die Benutzeroberfläche eingefroren zu sein scheint. Dies liegt daran, dass der asyncAnruf aktiviert istDispatchQueue.main

Vipin
quelle
0

GCDermöglicht es Ihnen, eine Aufgabe auszuführen synchronouslyoder asynchronously[Info] [Mehr]

synchronousDie Funktion (Blockieren und Warten) gibt eine Steuerung zurück, wann die Aufgabe abgeschlossen ist

asynchronousDie Funktion (Versand und Fortfahren) gibt sofort ein Steuerelement zurück, das die Aufgabe zum Starten in eine entsprechende Warteschlange sendet, aber nicht darauf wartet, dass sie abgeschlossen wird.

yoAlex5
quelle