iOS
GCD (Grand Central Dispatch)
Sök…
Introduktion
Grand Central Dispatch (GCD) är Apples svar på multithreading. Det är ett lätt ramverk för att utföra uppgifter synkront eller asynkront i köer och hanterar CPU-trådar för dig bakom kulisserna.
Relaterat ämne: Samtidighet
Skapa en sändningskö
Du kan skapa din egen kö med dispatch_queue_create
Objective-C
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
Snabb
// 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
Få huvudkön
Huvudkön är utsändningskön där alla UI-uppdateringar sker och koden för UI-ändringar placeras.
Du måste komma till huvudkön för att uppdatera användargränssnittet efter avslutad en asynkron process som NSURLSession
Det finns två typer av huvudkösamtal synchronous
och asynchronous
. När du åberopar något synchronously
betyder det att tråden som initierade den operationen kommer att vänta på att uppgiften ska slutföras innan du fortsätter. Asynchronous
innebär att den inte kommer att vänta.
Kodmål-C
Synchronous
huvudkösamtal
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Asynchronous
huvudkösamtal
dispatch_async(dispatch_get_main_queue(), ^{
// do work here to Usually to update the User Interface
});
SWIFT 3
Asynchronous
huvudkösamtal
DispatchQueue.main.async {
}
Synchronous
huvudkösamtal
DispatchQueue.main.sync {
}
Fraktgrupp
DispatchGroup möjliggör en samlad synkronisering av arbetet. Du kan använda dem för att skicka in flera olika arbetsobjekt och spåra när alla är klara, även om de kanske körs i olika köer. Detta beteende kan vara till hjälp när man inte kan göra framsteg förrän alla angivna uppgifter är slutförda.
Ett scenario när detta kan vara användbart är om du har flera webbtjänstsamtal som alla måste slutföras innan du fortsätter. Till exempel måste du ladda ner flera uppsättningar data som måste behandlas av någon funktion. Du måste vänta på att alla webbtjänster ska slutföras innan du ringer funktionen för att bearbeta alla mottagna data.
Snabb 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!")
}
Alternativt, om du inte vill vänta på att grupperna ska slutföras, men istället vill köra en funktion när alla uppgifter har slutförts, använd notify
i stället för 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
}
Exempel på utgång:
starting long running tasks
long task 0 done!
long task 3 done!
long task 1 done!
long task 2 done!
all tasks done!
Mer information finns i Apple Docs eller det relaterade ämnet
Skicka Semaphore
DispatchSemaphore tillhandahåller en effektiv implementering av en traditionell räknesemafor, som kan användas för att kontrollera åtkomst till en resurs över flera exekveringssituationer.
Ett scenario för när du ska använda en semafor kan vara om du gör en filläsning / -skrivning, om flera uppgifter försöker läsa och skriva från fil på samma gång kan det öka din prestanda så att varje uppgift väntar sin tur så att för att inte överbelasta I / O-kontrollen.
Snabb 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
}
}
}
Exempel på utgång: (märk tidstämplarna)
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
Mer information finns i Apple Docs
Seriella vs samtidiga köer
Snabb 3
Seriekö
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)")
}
}
}
Exempel på utgång:
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
Samtidig kö
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)")
}
}
}
Exempel på utgång:
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
Som vi kan se av exemplen ovan kommer en seriekö att slutföra varje uppgift i den ordning de skickas till kön. Varje uppgift väntar på att den föregående uppgiften är klar innan den körs. När det gäller den samtidiga kön väntar inte varje uppgift på de andra i kön och körs så snart som möjligt; fördelen är att alla uppgifter i kön kommer att köras samtidigt på separata trådar, vilket gör att en samtidskön tar mindre tid än en seriekö.
Om ordning för utförande av uppgifter inte är viktigt, använd alltid en samtidig kö för bästa effektivitet.