Swift Language
Parallelität
Suche…
Syntax
Swift 3,0
DispatchQueue.main // Ruft die Hauptwarteschlange ab
DispatchQueue (Label: "my-serial-queue", Attribute: [.serial, .qosBackground]) // Erstellen Sie Ihre eigene private serielle Warteschlange
DispatchQueue.global (Attribute: [.qosDefault]) // Greifen Sie auf eine der globalen gleichzeitigen Warteschlangen zu
DispatchQueue.main.async {...} // Eine Task asynchron zum Hauptthread ausgeben
DispatchQueue.main.sync {...} // Eine Aufgabe synchron an den Haupt-Thread senden
DispatchQueue.main.asyncAfter (Deadline: .now () + 3) {...} // Sendet eine Task asynchron an den Haupt-Thread, der nach x Sekunden ausgeführt wird
Schnell <3,0
dispatch_get_main_queue () // Liefert die Hauptwarteschlange im Hauptthread
dispatch_get_global_queue (dispatch_queue_priority_t, 0) // Globale Warteschlange mit der angegebenen Priorität abrufen dispatch_queue_priority_t
dispatch_async (dispatch_queue_t) {() -> In ... ungültig machen} // Eine Aufgabe asynchron auf dem angegebenen dispatch_queue_t ausgeben
dispatch_sync (dispatch_queue_t) {() -> In ... ungültig machen} // Eine Aufgabe synchron auf dem angegebenen dispatch_queue_t ausgeben
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, Int64 (Nanosekunden)), dispatch_queue_t, {...}); // Eine Aufgabe für den angegebenen dispatch_queue_t nach Nanosekunden ausgeben
Eine Grand Central Dispatch-Warteschlange (GCD) erhalten
Grand Central Dispatch arbeitet nach dem Konzept von "Dispatch Queues". Eine Versandwarteschlange führt die von Ihnen festgelegten Aufgaben in der Reihenfolge aus, in der sie übergeben werden. Es gibt drei Arten von Versandwarteschlangen:
- Serial Dispatch Queues (auch als private Dispatch Queues bezeichnet) führen eine Aufgabe nacheinander aus. Sie werden häufig verwendet, um den Zugriff auf eine Ressource zu synchronisieren.
- Concurrent Dispatch Queues (globale Dispatch Queues) führen eine oder mehrere Aufgaben gleichzeitig aus.
- Die Main Dispatch Queue führt Aufgaben im Hauptthread aus.
So greifen Sie auf die Hauptwarteschlange zu:
let mainQueue = DispatchQueue.main
let mainQueue = dispatch_get_main_queue()
Das System stellt gleichzeitige globale Versandwarteschlangen (global für Ihre Anwendung) mit unterschiedlichen Prioritäten bereit. Sie können auf diese Warteschlangen mit der DispatchQueue
Klasse in Swift 3 zugreifen:
let globalConcurrentQueue = DispatchQueue.global(qos: .default)
gleichwertig
let globalConcurrentQueue = DispatchQueue.global()
let globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
In iOS 8 oder höher sind die möglichen Servicequalitätswerte möglicherweise .userInteractive
, .userInitiated
, .default
, .utility
und .background
. Diese ersetzen die Konstanten DISPATCH_QUEUE_PRIORITY_
.
Sie können auch eigene Warteschlangen mit unterschiedlichen Prioritäten erstellen:
let myConcurrentQueue = DispatchQueue(label: "my-concurrent-queue", qos: .userInitiated, attributes: [.concurrent], autoreleaseFrequency: .workItem, target: nil)
let mySerialQueue = DispatchQueue(label: "my-serial-queue", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil)
let myConcurrentQueue = dispatch_queue_create("my-concurrent-queue", DISPATCH_QUEUE_CONCURRENT)
let mySerialQueue = dispatch_queue_create("my-serial-queue", DISPATCH_QUEUE_SERIAL)
In Swift 3 sind mit diesem Initialisierer erstellte Warteschlangen standardmäßig seriell. Durch die Weitergabe von .workItem
für die Autorelease-Häufigkeit wird sichergestellt, dass für jedes Arbeitselement ein Autorelease-Pool erstellt und entleert wird. Es gibt auch .never
, was bedeutet, dass Sie Ihre eigenen Autorelease-Pools selbst verwalten, oder .inherit
, das die Einstellung von der Umgebung erbt. In den meisten Fällen verwenden Sie .never
außer bei extremer Anpassung.
Ausführen von Aufgaben in einer Grand Central Dispatch-Warteschlange (GCD)
Verwenden Sie zum Ausführen von Aufgaben in einer Versandwarteschlange die Methoden sync
, async
und after
.
So senden Sie eine Aufgabe asynchron in eine Warteschlange:
let queue = DispatchQueue(label: "myQueueName")
queue.async {
//do something
DispatchQueue.main.async {
//this will be called in main thread
//any UI updates should be placed here
}
}
// ... code here will execute immediately, before the task finished
So senden Sie eine Aufgabe synchron in eine Warteschlange:
queue.sync {
// Do some task
}
// ... code here will not execute until the task is finished
So senden Sie eine Aufgabe nach einer bestimmten Anzahl von Sekunden in eine Warteschlange:
queue.asyncAfter(deadline: .now() + 3) {
//this will be executed in a background-thread after 3 seconds
}
// ... code here will execute immediately, before the task finished
HINWEIS: Alle Aktualisierungen der Benutzeroberfläche sollten im Hauptthread aufgerufen werden! Stellen Sie sicher, dass Sie den Code für UI-Aktualisierungen in
DispatchQueue.main.async { ... }
eingeben.
Warteschlangenarten:
let mainQueue = dispatch_get_main_queue()
let highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
So senden Sie eine Aufgabe asynchron in eine Warteschlange:
dispatch_async(queue) {
// Your code run run asynchronously. Code is queued and executed
// at some point in the future.
}
// Code after the async block will execute immediately
So senden Sie eine Aufgabe synchron in eine Warteschlange:
dispatch_sync(queue) {
// Your sync code
}
// Code after the sync block will wait until the sync task finished
Um eine Aufgabe nach einem Zeitintervall NSEC_PER_SEC
(verwenden Sie NSEC_PER_SEC
, um Sekunden in Nanosekunden umzuwandeln):
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// Code to be performed in 2.5 seconds here
}
So führen Sie eine Task asynchron aus und aktualisieren die Benutzeroberfläche:
dispatch_async(queue) {
// Your time consuming code here
dispatch_async(dispatch_get_main_queue()) {
// Update the UI code
}
}
HINWEIS: Alle Aktualisierungen der Benutzeroberfläche sollten im Hauptthread aufgerufen werden! Stellen Sie sicher, dass Sie den Code für UI-Aktualisierungen in
dispatch_async(dispatch_get_main_queue()) { ... }
eingeben.
Gleichzeitige Schleifen
GCD stellt einen Mechanismus zum Durchführen einer Schleife bereit, wobei die Schleifen gleichzeitig in Bezug zueinander stattfinden. Dies ist sehr nützlich, wenn Sie eine Reihe von rechenintensiven Berechnungen durchführen.
Betrachten Sie diese Schleife:
for index in 0 ..< iterations {
// Do something computationally expensive here
}
Sie können diese Berechnungen gleichzeitig mit concurrentPerform
(in Swift 3) oder dispatch_apply
(in Swift 2) ausführen:
DispatchQueue.concurrentPerform(iterations: iterations) { index in
// Do something computationally expensive here
}
dispatch_apply(iterations, queue) { index in
// Do something computationally expensive here
}
Der Schleifenschluss wird für jeden index
von 0
bis einschließlich iterations
aufgerufen. Diese Iterationen werden in Bezug zueinander gleichzeitig ausgeführt, und daher ist die Reihenfolge, in der sie ausgeführt werden, nicht garantiert. Die tatsächliche Anzahl von Iterationen, die zu einem bestimmten Zeitpunkt gleichzeitig ablaufen, wird im Allgemeinen durch die Fähigkeiten des betreffenden Geräts bestimmt (z. B. wie viele Kerne das Gerät hat).
Einige besondere Überlegungen:
concurrentPerform
/dispatch_apply
kann die Schleifen gleichzeitig ausführen, dies geschieht jedoch synchron mit dem Thread, von dem aus Sie es aufrufen. Rufen Sie das also nicht aus dem Haupt-Thread auf, da dieser den Thread blockiert, bis die Schleife abgeschlossen ist.Da diese Schleifen gleichzeitig stattfinden, sind Sie für die Gewindesicherheit der Ergebnisse verantwortlich. Wenn Sie beispielsweise ein Wörterbuch mit den Ergebnissen dieser rechnerisch teuren Berechnungen aktualisieren, müssen Sie diese Aktualisierungen selbst synchronisieren.
Beachten Sie, dass das Ausführen von gleichzeitigen Schleifen mit einem gewissen Aufwand verbunden ist. Wenn die Berechnungen, die innerhalb der Schleife ausgeführt werden, nicht rechenintensiv sind, kann es vorkommen, dass die durch die Verwendung von gleichzeitigen Schleifen erzielte Leistung durch den mit der Synchronisierung all dieser gleichzeitigen Threads verbundenen Overhead verringert, wenn nicht vollständig versetzt wird.
Sie sind also dafür verantwortlich, die richtige Menge an Arbeit zu bestimmen, die in jeder Iteration der Schleife ausgeführt werden muss. Wenn die Berechnungen zu einfach sind, können Sie "schreiten" verwenden, um mehr Arbeit pro Schleife hinzuzufügen. Anstatt eine gleichzeitige Schleife mit 1 Million trivialer Berechnungen durchzuführen, können Sie beispielsweise 100 Iterationen in Ihrer Schleife ausführen und 10.000 Berechnungen pro Schleife ausführen. Auf diese Weise wird für jeden Thread genügend Arbeit ausgeführt, sodass der mit der Verwaltung dieser gleichzeitigen Schleifen verbundene Aufwand geringer wird.
Ausführen von Aufgaben in einer OperationsQueue
Sie können sich eine OperationQueue
als eine Reihe von Aufgaben vorstellen, die auf ihre Ausführung warten. Im Gegensatz zu Versandwarteschlangen in GCD sind Operationswarteschlangen kein FIFO (first-in-first-out). Stattdessen führen sie Aufgaben aus, sobald sie zur Ausführung bereit sind, sofern genügend Systemressourcen zur Verfügung stehen.
Holen Sie sich die OperationQueue
:
let mainQueue = OperationQueue.main
Erstellen Sie eine benutzerdefinierte OperationQueue
:
let queue = OperationQueue()
queue.name = "My Queue"
queue.qualityOfService = .default
Die Dienstqualität legt fest, wie wichtig die Arbeit ist oder wie sehr der Benutzer wahrscheinlich auf die unmittelbaren Ergebnisse der Aufgabe zählen wird.
Hinzufügen einer Operation
zu einer OperationQueue
:
// An instance of some Operation subclass
let operation = BlockOperation {
// perform task here
}
queue.addOperation(operation)
Fügen Sie einer OperationQueue
einen Block hinzu:
myQueue.addOperation {
// some task
}
Mehrere Operation
s zu einer OperationQueue
:
let operations = [Operation]()
// Fill array with Operations
myQueue.addOperation(operations)
Passen Sie an, wie viele Operation
gleichzeitig in der Warteschlange ausgeführt werden dürfen:
myQueue.maxConcurrentOperationCount = 3 // 3 operations may execute at once
// Sets number of concurrent operations based on current system conditions
myQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
Durch das Anhalten einer Warteschlange wird verhindert, dass vorhandene, nicht gestartete Vorgänge oder neue, der Warteschlange hinzugefügte Vorgänge ausgeführt werden. isSuspended
diese Warteschlange wieder aufzunehmen, setzen Sie isSuspended
auf false
:
myQueue.isSuspended = true
// Re-enable execution
myQueue.isSuspended = false
Durch das Anhalten einer OperationQueue
Vorgänge, die bereits ausgeführt werden, nicht angehalten oder abgebrochen. Sie sollten nur versuchen, eine von Ihnen erstellte Warteschlange auszusetzen, keine globalen Warteschlangen oder die Hauptwarteschlange.
Erstellen von High-Level-Operationen
Das Foundation-Framework stellt den Operation
bereit, der ein übergeordnetes Objekt darstellt, das einen Teil der Arbeit enthält, der in einer Warteschlange ausgeführt werden kann. Die Warteschlange koordiniert nicht nur die Leistung dieser Vorgänge, Sie können auch Abhängigkeiten zwischen Vorgängen einrichten, stornierbare Vorgänge erstellen, den Parallelitätsgrad der Vorgangswarteschlange einschränken usw.
Operation
zur Ausführung bereit, wenn alle ihre Abhängigkeiten abgeschlossen sind. Die isReady
Eigenschaft ändert sich dann in true
.
Erstellen Sie eine einfache nicht gleichzeitig ablaufende Operation
Unterklasse:
class MyOperation: Operation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
class MyOperation: NSOperation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
Hinzufügen einer Operation zu einer OperationQueue
:
myQueue.addOperation(operation)
Dadurch wird die Operation in der Warteschlange gleichzeitig ausgeführt.
Verwalten Sie Abhängigkeiten von einer Operation
.
Abhängigkeiten definieren andere Operation
, die in einer Warteschlange ausgeführt werden müssen, bevor der Operation
als bereit zur Ausführung betrachtet wird.
operation2.addDependency(operation1)
operation2.removeDependency(operation1)
Ausführen einer Operation
ohne Warteschlange:
operation.start()
Abhängigkeiten werden ignoriert. Wenn dies ein gleichzeitiger Betrieb ist, kann immer noch die Aufgabe , gleichzeitig ausgeführt werden , wenn seine start
Arbeit Hintergrund Warteschlangen auslagert.
Gleichzeitige Vorgänge.
Wenn die Aufgabe, die eine Operation
ausführen soll, selbst asynchron ist (z. B. eine URLSession
Datenaufgabe), sollten Sie die Operation
als gleichzeitige Operation implementieren. In diesem Fall Ihre isAsynchronous
sollte Implementierung zurückkehren true
, Sie haben in der Regel eine start
, die einige Setup ausführt, ruft dann seine main
das die Aufgabe tatsächlich ausführt.
Zu Beginn der Implementierung einer asynchronen Operation
müssen isFinished
Methoden isExecuting
, isFinished
und KVO implementieren. Wenn die Ausführung startet, isExecuting
Eigenschaft isExecuting
in true
. Wenn ein Operation
seine Aufgabe beendet, wird isExecuting
auf false
und isFinished
auf true
. Wenn der Vorgang abgebrochen wird, ändern sich sowohl isCancelled
als auch isFinished
zu true
. Alle diese Eigenschaften sind beobachtbar.
Operation
abbrechen
Durch den Aufruf von cancel
wird die isCancelled
Eigenschaft einfach in true
isCancelled
. Um auf eine Stornierung aus Ihrer eigenen Operation
Unterklasse zu antworten, sollten Sie den Wert von isCancelled
mindestens einmal in main
überprüfen und entsprechend reagieren.
operation.cancel()