iOS
GCD (Grand Central Dispatch)
Ricerca…
introduzione
Grand Central Dispatch (GCD) è la risposta di Apple al multithreading. È un framework leggero per l'esecuzione di attività in modo sincrono o asincrono nelle code e gestisce i thread della CPU per te dietro le quinte.
Argomento correlato: concorrenza
Crea una coda di spedizione
Puoi creare la tua coda utilizzando dispatch_queue_create
Objective-C
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
veloce
// 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
Ottenere la coda principale
La coda principale è la coda di invio in cui si verificano tutti gli aggiornamenti dell'interfaccia utente e viene inserito il codice che comporta modifiche all'interfaccia utente.
È necessario raggiungere la coda principale per aggiornare l'interfaccia utente al termine di un processo asincrono come NSURLSession
Esistono due tipi di chiamate di coda principali synchronous
e asynchronous
. Quando invochi qualcosa in modo synchronously
, significa che il thread che ha avviato quell'operazione attenderà che l'attività finisca prima di continuare. Asynchronous
significa che non aspetterà.
Codice Obiettivo-C
Chiamata di coda principale Synchronous
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Chiamata di coda principale Asynchronous
dispatch_async(dispatch_get_main_queue(), ^{
// do work here to Usually to update the User Interface
});
SWIFT 3
Chiamata di coda principale Asynchronous
DispatchQueue.main.async {
}
Chiamata di coda principale Synchronous
DispatchQueue.main.sync {
}
Gruppo di spedizione
DispatchGroup consente la sincronizzazione aggregata del lavoro. Puoi usarli per inviare più articoli di lavoro diversi e tracciare quando tutti sono completi, anche se potrebbero essere eseguiti su code diverse. Questo comportamento può essere utile quando non è possibile eseguire progressi finché tutte le attività specificate non sono state completate.
Uno scenario in cui questo può essere utile è se hai più chiamate al webservice che devono essere completate prima di continuare. Ad esempio, è necessario scaricare più set di dati che devono essere elaborati da una funzione. È necessario attendere il completamento di tutti i servizi Web prima di chiamare la funzione per elaborare tutti i dati ricevuti.
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!")
}
In alternativa, se non si desidera attendere il completamento dei gruppi, ma si desidera eseguire una funzione al termine di tutte le attività, utilizzare la funzione di notify
al posto di 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
}
Esempio di output:
starting long running tasks
long task 0 done!
long task 3 done!
long task 1 done!
long task 2 done!
all tasks done!
Per maggiori informazioni, consultare Apple Docs o l' argomento correlato
Dispatch Semaphore
DispatchSemaphore fornisce un'implementazione efficiente di un semaforo di conteggio tradizionale, che può essere utilizzato per controllare l'accesso a una risorsa attraverso più contesti di esecuzione.
Uno scenario per quando utilizzare un semaforo potrebbe essere se stai facendo un po 'di lettura / scrittura di file, se più attività stanno cercando di leggere e scrivere dal file allo stesso tempo, potrebbe aumentare le tue prestazioni per far sì che ogni attività attenda il suo turno così come non sovraccaricare il controller I / O.
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
}
}
}
Esempio di output: (notare i timestamp)
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
Per maggiori informazioni, fare riferimento a Apple Docs
Code di invio seriale vs simultanee
Swift 3
Coda seriale
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)")
}
}
}
Esempio di output:
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
Coda concomitante
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)")
}
}
}
Esempio di output:
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
Discussione
Come possiamo vedere dagli esempi sopra, una coda seriale completerà ogni attività nell'ordine in cui sono inviati alla coda. Ogni attività attenderà il completamento dell'attività precedente prima dell'esecuzione. Per quanto riguarda la coda concorrente, ogni attività non attende gli altri nella coda ed esegue il prima possibile; il vantaggio è che tutte le attività in coda verranno eseguite contemporaneamente su thread separati, rendendo la coda simultanea meno tempo di una coda seriale.
Se l'ordine di esecuzione delle attività non è importante, utilizzare sempre una coda concorrente per la migliore efficienza.