iOS
GCD (Grand Central Dispatch)
Suche…
Einführung
Grand Central Dispatch (GCD) ist Apples Antwort auf Multithreading. Es handelt sich um ein einfaches Framework, um Aufgaben synchron oder asynchron in Warteschlangen auszuführen und CPU-Threads für Sie hinter den Kulissen zu verarbeiten.
Verwandte Themen: Parallelität
Erstellen Sie eine Versandwarteschlange
Sie können Ihre eigene Warteschlange mit dispatch_queue_create
erstellen
Ziel c
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
Schnell
// Before Swift 3
let queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL)
// Swift 3
let queue = DispatchQueue(label: "com.example.myqueue") //default is serial queue, unless .concurrent is specified as an attribute otherwise
Die Hauptwarteschlange abrufen
Die Hauptwarteschlange ist die Versandwarteschlange, in der alle Aktualisierungen der Benutzeroberfläche stattfinden und der Code, der Änderungen an der Benutzeroberfläche beinhaltet, platziert wird.
Sie müssen zur Hauptwarteschlange gelangen, um die Benutzeroberfläche nach Abschluss eines asynchronen Prozesses wie NSURLSession
Es gibt zwei Arten von Hauptwarteschlangenaufrufen, synchronous
und asynchronous
. Wenn Sie etwas synchronously
aufrufen, bedeutet dies, dass der Thread, der diesen Vorgang initiiert hat, auf die Beendigung der Aufgabe wartet, bevor Sie fortfahren. Asynchronous
bedeutet, dass es nicht wartet.
Code Objective-C
Synchronous
Aufruf der Hauptwarteschlange
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Asynchronous
Aufruf der Hauptwarteschlange
dispatch_async(dispatch_get_main_queue(), ^{
// do work here to Usually to update the User Interface
});
SWIFT 3
Asynchronous
Aufruf der Hauptwarteschlange
DispatchQueue.main.async {
}
Synchronous
Aufruf der Hauptwarteschlange
DispatchQueue.main.sync {
}
Versandgruppe
DispatchGroup ermöglicht die Aggregation der Arbeitssynchronisation. Sie können sie verwenden, um mehrere verschiedene Arbeitselemente zu senden und zu verfolgen, wann alle abgeschlossen sind, auch wenn sie in verschiedenen Warteschlangen ausgeführt werden. Dieses Verhalten kann hilfreich sein, wenn keine Fortschritte erzielt werden können, bis alle angegebenen Aufgaben abgeschlossen sind.
Ein Szenario, in dem dies nützlich sein kann, ist, wenn Sie mehrere Webservice-Aufrufe haben, die alle beendet werden müssen, bevor Sie fortfahren. Beispielsweise müssen Sie mehrere Datensätze herunterladen, die von einer Funktion verarbeitet werden müssen. Sie müssen warten, bis alle Webservices abgeschlossen sind, bevor Sie die Funktion aufrufen, um alle empfangenen Daten zu verarbeiten.
Swift 3
func doLongTasksAndWait () {
print("starting long running tasks")
let group = DispatchGroup() //create a group for a bunch of tasks we are about to do
for i in 0...3 { //launch a bunch of tasks (eg a bunch of webservice calls that all need to be finished before proceeding to the next ViewController)
group.enter() //let the group know that something is being added
DispatchQueue.global().async { //run tasks on a background thread
sleep(arc4random() % 4) //do some long task eg webservice or database lookup (here we are just sleeping for a random amount of time for demonstration purposes)
print("long task \(i) done!")
group.leave() //let group know that the task is finished
}
}
group.wait() //will block whatever thread we are on here until all the above tasks have finished (so maybe dont use this function on your main thread)
print("all tasks done!")
}
Wenn Sie nicht warten möchten, bis die Gruppen abgeschlossen sind, sondern stattdessen eine Funktion ausführen möchten, nachdem alle Aufgaben abgeschlossen sind, verwenden Sie die notify
anstelle der group.wait()
group.notify(queue: DispatchQueue.main) { //the queue: parameter is which queue this block will run on, if you need to do UI updates, use the main queue
print("all tasks done!") //this will execute when all tasks have left the group
}
Beispielausgabe:
starting long running tasks
long task 0 done!
long task 3 done!
long task 1 done!
long task 2 done!
all tasks done!
Weitere Informationen finden Sie in den Apple Docs oder im verwandten Thema
Dispatch Semaphore
DispatchSemaphore bietet eine effiziente Implementierung eines traditionellen Zählsemaphors, mit dem der Zugriff auf eine Ressource über mehrere Ausführungskontexte gesteuert werden kann.
Ein Szenario für die Verwendung eines Semaphors kann beispielsweise beim Lesen / Schreiben von Dateien auftreten. Wenn mehrere Tasks gleichzeitig aus einer Datei lesen und schreiben möchten, kann dies die Leistung erhöhen, wenn jede Task warten muss um den E / A-Controller nicht zu überlasten.
Swift 3
func do2TasksAtATime () {
print("starting long running tasks (2 at a time)")
let sem = DispatchSemaphore(value: 2) //this semaphore only allows 2 tasks to run at the same time (the resource count)
for i in 0...7 { //launch a bunch of tasks
DispatchQueue.global().async { //run tasks on a background thread
sem.wait() //wait here if no resources available
sleep(2) //do some long task eg file access (here we are just sleeping for a 2 seconds for demonstration purposes)
print("long task \(i) done! \(Date())")
sem.signal() //let the semaphore know this resource is now available
}
}
}
Beispielausgabe: (Beachten Sie die Zeitstempel)
starting long running tasks (2 at a time)
long task 0 done! 2017-02-16 07:11:53 +0000
long task 1 done! 2017-02-16 07:11:53 +0000
long task 2 done! 2017-02-16 07:11:55 +0000
long task 3 done! 2017-02-16 07:11:55 +0000
long task 5 done! 2017-02-16 07:11:57 +0000
long task 4 done! 2017-02-16 07:11:57 +0000
long task 6 done! 2017-02-16 07:11:59 +0000
long task 7 done! 2017-02-16 07:11:59 +0000
Weitere Informationen finden Sie in den Apple Docs
Serielle versus gleichzeitige Dispatch-Warteschlangen
Swift 3
Serienwarteschlange
func serialQueues () {
let serialQueue = DispatchQueue(label: "com.example.serial") //default queue type is a serial queue
let start = Date ()
for i in 0...3 { //launch a bunch of tasks
serialQueue.async { //run tasks on a background thread, using our serial queue
sleep(2) //do some long task eg webservice or database lookup
let timeTaken = Date().timeIntervalSince(start)
print("serial long task \(i) done! total time taken: \(timeTaken)")
}
}
}
Beispielausgabe:
serial long task 0 done! total time taken: 2.07241100072861
serial long task 1 done! total time taken: 4.16347700357437
serial long task 2 done! total time taken: 6.23209798336029
serial long task 3 done! total time taken: 8.30682599544525
Gleichzeitige Warteschlange
func concurrentQueues () {
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent) //explicitly specify the queue to be a concurrent queue
let start = Date ()
for i in 0...3 { //launch a bunch of tasks
concurrentQueue.async { //run tasks on a background thread, using our concurrent queue
sleep(2) //do some long task eg webservice or database lookup
let timeTaken = Date().timeIntervalSince(start)
print("concurrent long task \(i) done! total time taken: \(timeTaken)")
}
}
}
Beispielausgabe:
concurrent long task 3 done! total time taken: 2.07092100381851
concurrent long task 0 done! total time taken: 2.07087397575378
concurrent long task 2 done! total time taken: 2.07086700201035
concurrent long task 1 done! total time taken: 2.07089096307755
Diskussion
Wie aus den obigen Beispielen ersichtlich, wird eine serielle Warteschlange jede Aufgabe in der Reihenfolge abschließen, in der sie an die Warteschlange übergeben werden. Jede Aufgabe wartet, bis die vorherige Aufgabe abgeschlossen ist, bevor sie ausgeführt wird. Bei der gleichzeitigen Warteschlange wartet jede Aufgabe nicht auf die anderen in der Warteschlange und wird so bald wie möglich ausgeführt. Der Vorteil ist, dass alle Tasks in der Warteschlange gleichzeitig in separaten Threads ausgeführt werden, sodass eine gleichzeitige Warteschlange weniger Zeit als eine serielle Warteschlange benötigt.
Wenn die Reihenfolge der Ausführung von Aufgaben nicht wichtig ist, verwenden Sie immer eine gleichzeitige Warteschlange, um die beste Effizienz zu erzielen.