Ich habe Probleme, die gleichzeitigen und seriellen Warteschlangen in GCD vollständig zu verstehen. Ich habe einige Probleme und hoffe, dass mir jemand klar und genau antworten kann.
Ich lese, dass serielle Warteschlangen erstellt und verwendet werden, um Aufgaben nacheinander auszuführen. Was passiert jedoch, wenn:
- Ich erstelle eine serielle Warteschlange
- Ich verwende
dispatch_async
(in der soeben erstellten seriellen Warteschlange) dreimal, um drei Blöcke A, B, C zu versenden
Werden die drei Blöcke ausgeführt:
in der Reihenfolge A, B, C, da die Warteschlange seriell ist
ODER
- gleichzeitig (zur gleichen Zeit auf Parralel-Threads), weil ich ASYNC-Versand verwendet habe
Ich lese, dass ich
dispatch_sync
für gleichzeitige Warteschlangen verwenden kann, um Blöcke nacheinander auszuführen. WARUM gibt es in diesem Fall überhaupt serielle Warteschlangen, da ich immer eine gleichzeitige Warteschlange verwenden kann, in der ich SYNCHRON so viele Blöcke versenden kann, wie ich möchte?Vielen Dank für jede gute Erklärung!
quelle
Antworten:
Ein einfaches Beispiel: Sie haben einen Block, dessen Ausführung eine Minute dauert. Sie fügen es einer Warteschlange aus dem Hauptthread hinzu. Schauen wir uns die vier Fälle an.
Offensichtlich würden Sie keine der beiden letzten für lange laufende Prozesse verwenden. Normalerweise wird dies angezeigt, wenn Sie versuchen, die Benutzeroberfläche (immer im Hauptthread) von etwas zu aktualisieren, das möglicherweise auf einem anderen Thread ausgeführt wird.
quelle
Hier sind einige Experimente, die ich durchgeführt habe, um mich über diese zu informieren
serial
,concurrent
Warteschlangen mitGrand Central Dispatch
.Hier ist eine Zusammenfassung dieser Experimente
Denken Sie daran, dass Sie mit GCD nur Aufgaben zur Warteschlange hinzufügen und Aufgaben aus dieser Warteschlange ausführen. Die Warteschlange sendet Ihre Aufgabe entweder im Haupt- oder im Hintergrundthread, je nachdem, ob der Vorgang synchron oder asynchron ist. Warteschlangentypen sind Seriell, Gleichzeitig, Hauptversandwarteschlange. Alle von Ihnen ausgeführten Aufgaben werden standardmäßig über die Hauptversandwarteschlange ausgeführt. Es gibt bereits vier vordefinierte globale gleichzeitige Warteschlangen für Ihre Anwendung und eine Hauptwarteschlange (DispatchQueue.main) Sie können auch manuell eine eigene Warteschlange erstellen und Aufgaben aus dieser Warteschlange ausführen.
UI-bezogene Aufgaben sollten immer vom Hauptthread aus ausgeführt werden, indem die Aufgabe an die Hauptwarteschlange gesendet wird
DispatchQueue.main.sync/async
während netzwerkbezogene / schwere Vorgänge immer asynchron ausgeführt werden sollten, davon, welchen Thread Sie entweder als Haupt- oder als Hintergrund verwendenBEARBEITEN: Es gibt jedoch Fälle, in denen Sie Netzwerkaufrufvorgänge synchron in einem Hintergrundthread ausführen müssen, ohne die Benutzeroberfläche einzufrieren (z. B. OAuth-Token aktualisieren und warten, ob dies erfolgreich ist oder nicht). Sie müssen diese Methode in einen asynchronen Vorgang einbinden Operationen werden in der Reihenfolge und ohne Blockieren des Hauptthreads ausgeführt.
EDIT EDIT: Sie können Demo - Video sehen hier
quelle
}
da er in diesem Moment wirklich nicht ausgeführt wirdconcurrentQueue.sync
vondoLongSyncTaskInConcurrentQueue()
Funktion, sie druckt den Haupt-Thread,Task will run in different thread
scheint nicht wahr zu sein.Zunächst ist es wichtig, den Unterschied zwischen Threads und Warteschlangen zu kennen und zu wissen, was GCD wirklich tut. Wenn wir Versandwarteschlangen (über GCD) verwenden, stehen wir wirklich in der Warteschlange und nicht im Threading. Das Dispatch-Framework wurde speziell entwickelt, um uns vom Threading abzuhalten, da Apple zugibt, dass "die Implementierung einer korrekten Threading-Lösung extrem schwierig, wenn nicht [manchmal] unmöglich werden kann". Um Aufgaben gleichzeitig auszuführen (Aufgaben, bei denen die Benutzeroberfläche nicht eingefroren werden soll), müssen wir lediglich eine Warteschlange dieser Aufgaben erstellen und an GCD übergeben. Und GCD übernimmt das gesamte zugehörige Threading. Deshalb stellen wir uns nur in die Warteschlange.
Das zweite, was Sie sofort wissen müssen, ist, was eine Aufgabe ist. Eine Aufgabe ist der gesamte Code in diesem Warteschlangenblock (nicht in der Warteschlange, da wir einer Warteschlange jederzeit Dinge hinzufügen können, sondern innerhalb des Abschlusses, in dem wir ihn der Warteschlange hinzugefügt haben). Eine Aufgabe wird manchmal als Block bezeichnet, und ein Block wird manchmal als Aufgabe bezeichnet (sie werden jedoch häufiger als Aufgaben bezeichnet, insbesondere in der Swift-Community). Und egal wie viel oder wenig Code, der gesamte Code in den geschweiften Klammern wird als eine einzige Aufgabe betrachtet:
Und es ist offensichtlich zu erwähnen, dass gleichzeitig einfach gleichzeitig mit anderen Dingen bedeutet und seriell bedeutet nacheinander (niemals gleichzeitig). Etwas zu serialisieren oder etwas in Serie zu setzen bedeutet einfach, es von Anfang bis Ende in seiner Reihenfolge von links nach rechts, von oben nach unten und ohne Unterbrechung auszuführen.
Es gibt zwei Arten von Warteschlangen: serielle und gleichzeitige, aber alle Warteschlangen sind relativ zueinander gleichzeitig . Die Tatsache, dass Sie Code "im Hintergrund" ausführen möchten, bedeutet, dass Sie ihn gleichzeitig mit einem anderen Thread (normalerweise dem Hauptthread) ausführen möchten. Daher führen alle seriellen oder gleichzeitigen Versandwarteschlangen ihre Aufgaben gleichzeitig relativ zu anderen Warteschlangen aus . Jede von Warteschlangen (von seriellen Warteschlangen) durchgeführte Serialisierung hat nur mit den Aufgaben in dieser einzelnen [seriellen] Versandwarteschlange zu tun (wie im obigen Beispiel, in dem sich zwei Aufgaben in derselben seriellen Warteschlange befinden; diese Aufgaben werden nacheinander ausgeführt der andere, niemals gleichzeitig).
SERIENWARTEN (häufig als private Versandwarteschlangen bezeichnet) garantieren die Ausführung von Aufgaben nacheinander von Anfang bis Ende in der Reihenfolge, in der sie dieser bestimmten Warteschlange hinzugefügt wurden. Dies ist die einzige Garantie für die Serialisierung in der Diskussion der Versandwarteschlangen- dass die spezifischen Aufgaben innerhalb einer bestimmten seriellen Warteschlange seriell ausgeführt werden. Serielle Warteschlangen können jedoch gleichzeitig mit anderen seriellen Warteschlangen ausgeführt werden, wenn es sich um separate Warteschlangen handelt, da wiederum alle Warteschlangen relativ zueinander gleichzeitig sind. Alle Aufgaben werden auf unterschiedlichen Threads ausgeführt, aber nicht jede Aufgabe wird garantiert auf demselben Thread ausgeführt (nicht wichtig, aber interessant zu wissen). Und das iOS-Framework enthält keine gebrauchsfertigen seriellen Warteschlangen. Sie müssen diese erstellen. Private (nicht globale) Warteschlangen sind standardmäßig seriell. So erstellen Sie eine serielle Warteschlange:
Sie können es über die Attributeigenschaft gleichzeitig ausführen:
Wenn Sie der privaten Warteschlange jedoch keine weiteren Attribute hinzufügen, empfiehlt Apple, dass Sie nur eine der sofort einsatzbereiten globalen Warteschlangen verwenden (die alle gleichzeitig ausgeführt werden). Am Ende dieser Antwort sehen Sie eine andere Möglichkeit, serielle Warteschlangen (mithilfe der Zieleigenschaft) zu erstellen. Apple empfiehlt dies (für eine effizientere Ressourcenverwaltung). Die Kennzeichnung reicht jedoch vorerst aus.
CONCURRENT QUEUES (oft als globale Versandwarteschlangen bezeichnet) können Aufgaben gleichzeitig ausführen. Es wird jedoch garantiert, dass die Aufgaben in der Reihenfolge initiiert werden, in der sie zu dieser bestimmten Warteschlange hinzugefügt wurden. Im Gegensatz zu seriellen Warteschlangen wartet die Warteschlange jedoch nicht auf den Abschluss der ersten Aufgabe, bevor die zweite Aufgabe gestartet wird. Aufgaben (wie bei seriellen Warteschlangen) werden in unterschiedlichen Threads ausgeführt, und (wie bei seriellen Warteschlangen) wird nicht garantiert, dass jede Aufgabe auf demselben Thread ausgeführt wird (nicht wichtig, aber interessant zu wissen). Das iOS-Framework verfügt über vier sofort einsatzbereite Warteschlangen. Sie können eine gleichzeitige Warteschlange mithilfe des obigen Beispiels oder mithilfe einer der globalen Warteschlangen von Apple erstellen (was normalerweise empfohlen wird):
Es gibt zwei Möglichkeiten, Warteschlangen zu versenden: synchron und asynchron.
SYNC DISPATCHING bedeutet, dass der Thread, an den die Warteschlange gesendet wurde (der aufrufende Thread), nach dem Versenden der Warteschlange pausiert und wartet, bis die Ausführung der Aufgabe in diesem Warteschlangenblock abgeschlossen ist, bevor sie fortgesetzt wird. So versenden Sie synchron:
ASYNC DISPATCHING bedeutet, dass der aufrufende Thread nach dem Versenden der Warteschlange weiterhin ausgeführt wird und nicht darauf wartet, dass die Ausführung der Aufgabe in diesem Warteschlangenblock abgeschlossen ist. So senden Sie asynchron:
Nun könnte man denken, dass zum Ausführen einer Aufgabe in serieller Form eine serielle Warteschlange verwendet werden sollte, und das ist nicht genau richtig. Um mehrere Aufgaben seriell auszuführen , sollte eine serielle Warteschlange verwendet werden, aber alle Aufgaben (für sich isoliert) werden seriell ausgeführt. Betrachten Sie dieses Beispiel:
Unabhängig davon, wie Sie diese Warteschlange konfigurieren (seriell oder gleichzeitig) oder versenden (synchronisieren oder asynchronisieren), wird diese Aufgabe immer seriell ausgeführt. Die dritte Schleife wird niemals vor der zweiten Schleife ausgeführt, und die zweite Schleife wird niemals vor der ersten Schleife ausgeführt. Dies gilt für jede Warteschlange, die einen beliebigen Versand verwendet. Wenn Sie mehrere Aufgaben und / oder Warteschlangen einführen, kommen Serialität und Parallelität wirklich ins Spiel.
Betrachten Sie diese beiden Warteschlangen, eine serielle und eine gleichzeitige:
Angenommen, wir versenden zwei gleichzeitige Warteschlangen asynchron:
Ihre Ausgabe ist durcheinander (wie erwartet), aber beachten Sie, dass jede Warteschlange ihre eigene Aufgabe seriell ausführt. Dies ist das grundlegendste Beispiel für Parallelität - zwei Aufgaben, die gleichzeitig im Hintergrund in derselben Warteschlange ausgeführt werden. Jetzt machen wir die erste Serie:
Soll die erste Warteschlange nicht seriell ausgeführt werden? Es war (und so war das zweite). Was auch immer im Hintergrund passiert ist, ist für die Warteschlange nicht von Belang. Wir haben der seriellen Warteschlange gesagt, dass sie seriell ausgeführt werden soll, und das hat es getan ... aber wir haben ihr nur eine Aufgabe gegeben. Geben wir ihm nun zwei Aufgaben:
Dies ist das grundlegendste (und einzig mögliche) Beispiel für die Serialisierung - zwei Aufgaben, die seriell (nacheinander) im Hintergrund (zum Hauptthread) in derselben Warteschlange ausgeführt werden. Wenn wir sie jedoch zu zwei separaten seriellen Warteschlangen gemacht haben (da sie im obigen Beispiel dieselbe Warteschlange sind), wird ihre Ausgabe erneut durcheinander gebracht:
Und das habe ich gemeint, als ich sagte, dass alle Warteschlangen relativ zueinander gleichzeitig sind. Dies sind zwei serielle Warteschlangen, die ihre Aufgaben gleichzeitig ausführen (da es sich um separate Warteschlangen handelt). Eine Warteschlange kennt oder kümmert sich nicht um andere Warteschlangen. Kehren wir nun zu zwei seriellen Warteschlangen (derselben Warteschlange) zurück und fügen eine dritte Warteschlange hinzu, eine gleichzeitige:
Das ist etwas unerwartet. Warum hat die gleichzeitige Warteschlange gewartet, bis die seriellen Warteschlangen beendet sind, bevor sie ausgeführt wurden? Das ist keine Parallelität. Ihr Spielplatz zeigt möglicherweise eine andere Ausgabe, aber meine hat dies gezeigt. Dies zeigte sich, weil die Priorität meiner gleichzeitigen Warteschlange nicht hoch genug war, damit GCD ihre Aufgabe früher ausführen konnte. Wenn ich also alles gleich halte, aber die QoS der globalen Warteschlange (die Servicequalität, die einfach die Prioritätsstufe der Warteschlange darstellt) ändere
let concurrentQueue = DispatchQueue.global(qos: .userInteractive)
, ist die Ausgabe wie erwartet:Die beiden seriellen Warteschlangen haben ihre Aufgaben seriell ausgeführt (wie erwartet), und die gleichzeitige Warteschlange hat ihre Aufgabe schneller ausgeführt, da ihr eine hohe Priorität (hohe QoS oder Servicequalität) zugewiesen wurde.
Zwei gleichzeitige Warteschlangen, wie in unserem ersten Druckbeispiel, zeigen einen durcheinandergebrachten Ausdruck (wie erwartet). Damit sie ordentlich seriell gedruckt werden, müssen beide dieselbe serielle Warteschlange haben (dieselbe Instanz dieser Warteschlange, nicht nur dasselbe Etikett) . Dann wird jede Aufgabe seriell in Bezug auf die andere ausgeführt. Eine andere Möglichkeit, sie zum seriellen Drucken zu bewegen, besteht darin, beide gleichzeitig zu halten, aber ihre Versandmethode zu ändern:
Denken Sie daran, dass das Versenden der Synchronisierung nur bedeutet, dass der aufrufende Thread wartet, bis die Aufgabe in der Warteschlange abgeschlossen ist, bevor Sie fortfahren. Die Einschränkung hierbei ist natürlich, dass der aufrufende Thread eingefroren ist, bis die erste Aufgabe abgeschlossen ist. Dies kann die Art und Weise sein, wie die Benutzeroberfläche ausgeführt werden soll oder nicht.
Aus diesem Grund können wir Folgendes nicht tun:
Dies ist die einzig mögliche Kombination von Warteschlangen und Versandmethoden, die wir nicht ausführen können - synchrones Versenden in der Hauptwarteschlange. Und das liegt daran, dass wir die Hauptwarteschlange zum Einfrieren auffordern, bis wir die Aufgabe innerhalb der geschweiften Klammern ausführen ... die wir an die Hauptwarteschlange gesendet haben, die wir gerade eingefroren haben. Dies wird als Deadlock bezeichnet. Um es auf einem Spielplatz in Aktion zu sehen:
Eine letzte Sache zu erwähnen sind Ressourcen. Wenn wir einer Warteschlange eine Aufgabe zuweisen, findet GCD eine verfügbare Warteschlange aus dem intern verwalteten Pool. Für das Schreiben dieser Antwort stehen pro QOS 64 Warteschlangen zur Verfügung. Das mag viel erscheinen, kann aber schnell konsumiert werden, insbesondere von Bibliotheken von Drittanbietern, insbesondere von Datenbank-Frameworks. Aus diesem Grund hat Apple Empfehlungen zur Warteschlangenverwaltung (siehe Links unten). ein Wesen:
Um dies zu tun, empfiehlt Apple, anstatt sie wie zuvor zu erstellen (was Sie immer noch können), serielle Warteschlangen wie folgt zu erstellen:
Zur weiteren Lektüre empfehle ich Folgendes:
https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091-CH1-SW1
https://developer.apple.com/documentation/dispatch/dispatchqueue
quelle
Wenn ich mich richtig zu verstehen , wie GCD funktioniert, ich denke , es gibt zwei Arten von
DispatchQueue
,serial
undconcurrent
, zur gleichen Zeit, es zwei Wege gibt , wieDispatchQueue
ihre Aufgaben versenden, die zugeordneteclosure
, erste istasync
, und die andere istsync
. Diese zusammen bestimmen, wie der Abschluss (die Aufgabe) tatsächlich ausgeführt wird.Ich habe das gefunden
serial
undconcurrent
meine, wie viele Threads diese Warteschlange verwenden kann,serial
bedeutet eins, währendconcurrent
viele bedeutet. Undsync
undasync
bedeutet, dass die Aufgabe ausgeführt wird, auf welchem Thread, dem Thread des Aufrufers oder dem dieser Warteschlange zugrunde liegenden Thread, auf dem Thread des Aufrufers ausgeführt wird,sync
währendasync
auf dem zugrunde liegenden Thread ausgeführt wird.Das Folgende ist experimenteller Code, der auf dem Xcode-Spielplatz ausgeführt werden kann.
Hoffe es kann hilfreich sein.
quelle
Ich denke das gerne mit dieser Metapher (Hier ist der Link zum Originalbild):
Stellen wir uns vor, dein Vater macht den Abwasch und du hast gerade ein Glas Soda getrunken. Sie bringen das Glas zu Ihrem Vater, um es aufzuräumen, und stellen es neben das andere Gericht.
Jetzt erledigt Ihr Vater den Abwasch ganz alleine, also muss er ihn einzeln erledigen: Ihr Vater hier repräsentiert eine Serienwarteschlange .
Aber Sie sind nicht wirklich daran interessiert, dort zu stehen und zuzusehen, wie es aufgeräumt wird. Also lässt du das Glas fallen und gehst zurück in dein Zimmer: Dies wird als asynchroner Versand bezeichnet . Dein Vater könnte dich wissen lassen oder nicht, wenn er fertig ist, aber das Wichtigste ist, dass du nicht darauf wartest, dass das Glas aufgeräumt wird. Du gehst zurück in dein Zimmer, um Kinderkram zu erledigen.
Nehmen wir jetzt an, Sie haben immer noch Durst und möchten etwas Wasser auf demselben Glas haben, das zufällig Ihr Favorit ist, und Sie möchten es wirklich zurück, sobald es aufgeräumt ist. Also stehst du da und siehst zu, wie dein Vater den Abwasch macht, bis dein Geschirr fertig ist. Dies ist ein Synchronisierungsversand , da Sie blockiert sind, während Sie auf den Abschluss der Aufgabe warten.
Und zum Schluss sagen wir, deine Mutter beschließt, deinem Vater zu helfen und schließt sich ihm beim Abwasch an. Jetzt wird die Warteschlange zu einer gleichzeitigen Warteschlange, da mehrere Gerichte gleichzeitig gereinigt werden können. Beachten Sie jedoch, dass Sie immer noch entscheiden können, dort zu warten oder in Ihr Zimmer zurückzukehren, unabhängig davon, wie sie funktionieren.
Hoffe das hilft
quelle
1. Ich lese, dass serielle Warteschlangen erstellt und verwendet werden, um Aufgaben nacheinander auszuführen. Was passiert jedoch, wenn: • ich eine serielle Warteschlange erstelle • ich dispatch_async (in der soeben erstellten seriellen Warteschlange) dreimal verwende, um drei Blöcke A, B, C zu versenden
ANTWORT : - Alle drei Blöcke werden nacheinander ausgeführt. Ich habe einen Beispielcode erstellt, der das Verständnis erleichtert.
quelle