Bei Verwendung von GCD möchten wir warten, bis zwei asynchrone Blöcke ausgeführt und ausgeführt wurden, bevor wir mit den nächsten Ausführungsschritten fortfahren. Was ist der beste Weg das zu tun?
Wir haben Folgendes versucht, aber es scheint nicht zu funktionieren:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
});
// wait until both the block1 and block2 are done before start block3
// how to do that?
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
});
Antworten:
Verwenden Versandgruppen: siehe hier für ein Beispiel, „Waiting auf Gruppen von Queued Aufgaben“ in den „Dispatch - Queues“ Kapiteln von Apples iOS Developer Library Concurrency Programming Guide
Ihr Beispiel könnte ungefähr so aussehen:
und könnte eine Ausgabe wie diese erzeugen:
quelle
dispatch_group_async
ist genau wiedispatch_async
mit einem Gruppenparameter hinzugefügt. Wenn Sie also unterschiedliche Warteschlangen für Block1 und Block2 verwenden oder diese in derselben gleichzeitigen Warteschlange planen, können sie gleichzeitig ausgeführt werden. Wenn Sie sie in derselben seriellen Warteschlange planen, werden sie seriell ausgeführt. Es unterscheidet sich nicht von der Planung der Blöcke ohne Gruppen.Wenn Sie die Antwort von Jörn Eyrich erweitern (seine Antwort verbessern, wenn Sie diese beantworten),
dispatch_async
können Sie die GCD-Gruppen mitdispatch_group_enter
unddispatch_group_leave
direkt verwenden , wenn Sie nicht die Kontrolle über die Aufrufe Ihrer Blöcke haben, wie dies bei asynchronen Abschlussblöcken der Fall sein kann .In diesem Beispiel tun
computeInBackground
wir so, als könnten wir nichts ändern (stellen Sie sich vor, es handelt sich um einen Delegaten-Rückruf, einen NSURLConnection CompletionHandler oder was auch immer), und daher haben wir keinen Zugriff auf die Versandaufrufe.In diesem Beispiel wird computeInBackground: vervollständigung: wie folgt implementiert:
Ausgabe (mit Zeitstempeln aus einem Lauf):
quelle
dispatch_queue_notify
ist dies wahrscheinlich besser (es sei denn, die Blockierungszeit ist garantiert kurz).Mit Swift 5.1 bietet Grand Central Dispatch viele Möglichkeiten, um Ihr Problem zu lösen. Je nach Bedarf können Sie eines der sieben Muster auswählen , die in den folgenden Spielplatzausschnitten gezeigt werden.
# 1. Verwenden von
DispatchGroup
,DispatchGroup
'snotify(qos:flags:queue:execute:)
undDispatchQueue
' sasync(group:qos:flags:execute:)
Im Apple Developer Concurrency Programming Guide wird Folgendes beschrieben
DispatchGroup
:# 2. Verwenden von
DispatchGroup
,DispatchGroup
'swait()
,DispatchGroup
' senter()
undDispatchGroup
'sleave()
Beachten Sie, dass Sie auch
DispatchGroup
wait()
mitDispatchQueue
async(group:qos:flags:execute:)
oder mischenDispatchGroup
enter()
undDispatchGroup
leave()
mit mischen könnenDispatchGroup
notify(qos:flags:queue:execute:)
.#3. Verwenden von und 's
DispatchWorkItemFlags
barrier
DispatchQueue
async(group:qos:flags:execute:)
Grand Central Dispatch Tutorial für Swift 4: Teil 1/2 Artikel von Raywenderlich.com enthält eine Definition für Barrieren :
Verwendung:
# 4. Verwenden von
DispatchWorkItem
,DispatchWorkItemFlags
'sbarrier
undDispatchQueue
' sasync(execute:)
# 5. Verwenden von
DispatchSemaphore
,DispatchSemaphore
'swait()
undDispatchSemaphore
' ssignal()
Soroush Khanlou schrieb die folgenden Zeilen im Blog-Beitrag des GCD-Handbuchs :
Die Apple Developer API-Referenz enthält auch die folgende Diskussion für den
DispatchSemaphore
init(value:)
Initialisierer:Verwendung:
# 6. Verwenden von
OperationQueue
undOperation
'saddDependency(_:)
In der Apple Developer API-Referenz heißt es über
OperationQueue
:Verwendung:
# 7. Verwenden von
OperationQueue
undOperationQueue
'saddBarrierBlock(_:)
(erfordert iOS 13)quelle
Eine weitere GCD-Alternative ist eine Barriere:
Erstellen Sie einfach eine gleichzeitige Warteschlange, versenden Sie Ihre beiden Blöcke und versenden Sie dann den letzten Block mit Barriere, sodass Sie warten müssen, bis die beiden anderen fertig sind.
quelle
sleep()
! Ich habe diesesleep()
Aufrufe nur aus pädagogischen Gründen hinzugefügt , damit die Blöcke lange genug laufen, damit Sie sehen können, dass sie gleichzeitig laufen. In diesem trivialen Beispiel könnensleep()
diese beiden Blöcke in Abwesenheit von so schnell ausgeführt werden, dass der versendete Block möglicherweise gestartet und beendet wird, bevor Sie die Möglichkeit haben, die gleichzeitige Ausführung empirisch zu beobachten. Aber nichtsleep()
in Ihrem eigenen Code.Ich weiß, dass Sie nach GCD gefragt haben, aber wenn Sie wollten, können Sie
NSOperationQueue
diese Art von Dingen auch sehr anmutig behandeln, z.quelle
NSOperation
Unterklasse, die gleichzeitig ausgeführt wird,isFinished
wenn der asynchrone Prozess abgeschlossen ist. Dann funktionieren die Abhängigkeiten einwandfrei.dispatch_semaphore_wait
es nicht in der Hauptwarteschlange stattfindet und Ihre Signale und Wartezeiten ausgeglichen sind). Solange Sie die Hauptwarteschlange nicht blockieren, ist ein Semaphor-Ansatz in Ordnung, wenn Sie nicht die Flexibilität von Vorgängen benötigen (z. B. die Möglichkeit, sie abzubrechen, den Grad der Parallelität zu steuern usw.).maxConcurrentOperationCount
auf1
. Sie können auch die Priorität von Vorgängen festlegen, sowohl diequalityOfService
als auchqueuePriority
, aber diese haben einen weitaus subtileren Einfluss auf die Aufgabenpriorität als die Abhängigkeiten und / oder der Grad der Parallelität der Warteschlangen.Die obigen Antworten sind alle cool, aber sie haben alle eine Sache übersehen. group führt Aufgaben (Blöcke) in dem Thread aus, in den sie eingegeben wurden, wenn Sie
dispatch_group_enter
/ verwendendispatch_group_leave
.Dies wird in der erstellten gleichzeitigen Warteschlange ausgeführt
demoQueue
. Wenn ich keine Warteschlange erstelle, läuft sie im Hauptthread .und es gibt eine dritte Möglichkeit, Aufgaben in einem anderen Thread auszuführen:
Natürlich können Sie, wie bereits erwähnt, verwenden
dispatch_group_async
, um das zu bekommen, was Sie wollen.quelle
Die erste Antwort ist im Wesentlichen richtig. Wenn Sie jedoch den einfachsten Weg suchen, um das gewünschte Ergebnis zu erzielen, finden Sie hier ein eigenständiges Codebeispiel, das zeigt, wie dies mit einem Semaphor gemacht wird (wie auch Versandgruppen hinter den Kulissen arbeiten, JFYI). ::
quelle
dispatch_semaphore_wait
. Sie haben zwei Signale, also brauchen Sie zwei Wartezeiten. Wie es ist, beginnt Ihr "Abschluss" -Block, sobald der erste Block das Semaphor signalisiert, aber bevor der andere Block beendet ist. 2. Da dies eine iOS-Frage war, würde ich von der Verwendung von abratendispatch_main
.dispatch_semaphore_wait
wird entsperrt, sobald eine derdispatch_semaphore_signal
Methoden aufgerufen wird. Der Grund, warum dies zu funktionieren scheint, ist, dass dieprintf
for-Blöcke 'eins' und 'zwei' sofort auftreten und dieprintf
für 'endlich' nach einer Wartezeit auftreten - also nachdem der Block 2 Sekunden lang geschlafen hat. Wenn Sie den Ausdruck nach densleep
Aufrufen einfügen , erhalten Sie die Ausgabe für 'eins', dann 2 Sekunden später für 'endlich' und 2 Sekunden später für 'zwei'.Akzeptierte Antwort in Kürze:
quelle
Swift 4.2 Beispiel:
quelle
group.leave()
verursachte AbsturzUm nicht zu sagen, dass andere Antworten unter bestimmten Umständen nicht gut sind, aber dies ist ein Ausschnitt, den ich immer von Google benutze:
quelle