Swift Language
samtidighet
Sök…
Syntax
Swift 3.0
DispatchQueue.main // Få huvudkön
DispatchQueue (etikett: "my-serial-kö", attribut: [.serial, .qosBackground]) // Skapa din egen privata seriekö
DispatchQueue.global (attribut: [.qosDefault]) // Få åtkomst till en av de globala samtidiga köerna
DispatchQueue.main.async {...} // Skicka en uppgift asynkront till huvudtråden
DispatchQueue.main.sync {...} // Skicka en uppgift synkront till huvudtråden
DispatchQueue.main.asyncAfter (tidsfrist:. Nu () + 3) {...} // Skicka en uppgift asynkront till huvudtråden som ska köras efter x sekunder
Swift <3.0
dispatch_get_main_queue () // Få huvudkön som körs på huvudtråden
dispatch_get_global_queue (dispatch_queue_priority_t, 0) // Få global kö med specificerad prioritet dispatch_queue_priority_t
dispatch_async (dispatch_queue_t) {() -> Void in ...} // Skicka en uppgift asynkront på det angivna dispatch_queue_t
dispatch_sync (dispatch_queue_t) {() -> Void in ...} // Skicka en uppgift synkront på det angivna dispatch_queue_t
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, Int64 (nanosekunder)), dispatch_queue_t, {...}); // Skicka en uppgift på det angivna dispatch_queue_t efter nanosekunder
Få en Grand Central Dispatch (GCD) -kö
Grand Central Dispatch arbetar med konceptet "Dispatch Queues". En sändningskö kör uppgifter som du anger i den ordning de skickas. Det finns tre typer av sändningskö:
- Seriella utsändningsköer (aka privata utsändningsköer) utför en uppgift i taget i ordning. De används ofta för att synkronisera åtkomst till en resurs.
- Samtidiga sändningsköer (alias globala sändningsköer) utför en eller flera uppgifter samtidigt.
- Main Dispatch Queue kör uppgifter på huvudtråden.
För att komma åt huvudkön:
let mainQueue = DispatchQueue.main
let mainQueue = dispatch_get_main_queue()
Systemet tillhandahåller samtidig globala sändningsköer (globala för din applikation) med olika prioriteringar. Du kan komma åt dessa köer med klassen DispatchQueue
i Swift 3:
let globalConcurrentQueue = DispatchQueue.global(qos: .default)
ekvivalent med
let globalConcurrentQueue = DispatchQueue.global()
let globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
I iOS 8 eller senare är den möjliga kvaliteten på servicevärden som kan passeras .userInteractive
, .userInitiated
, .default
, .utility
och .background
. Dessa ersätter DISPATCH_QUEUE_PRIORITY_
konstanterna.
Du kan också skapa dina egna köer med olika prioriteringar:
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)
I Swift 3 är köer som skapats med denna initierare seriella som standard och passering av .workItem
för autoreleasfrekvens säkerställer att en autoreleasepool skapas och dräneras för varje arbetsobjekt. Det finns också .never
, vilket innebär att du kommer att hantera dina egna autoreleaspooler själv, eller. .inherit
som ärver inställningen från miljön. I de flesta fall kommer du antagligen inte att använda .never
utom i fall av extrem anpassning.
Köra uppgifter i en Grand Central Dispatch (GCD) -kö
För att köra uppgifter i en sändningskö använder du sync
, async
och after
metoder.
Så här skickar du en uppgift till en kö asynkront:
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
Så här skickar du en uppgift till en kö synkront:
queue.sync {
// Do some task
}
// ... code here will not execute until the task is finished
Så här skickar du en uppgift till en kö efter ett visst antal sekunder:
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
OBS: Eventuella uppdateringar av användargränssnittet ska anropas på huvudtråden! Se till att du lägger in koden för UI-uppdateringar i
DispatchQueue.main.async { ... }
Typer av kö:
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)
Så här skickar du en uppgift till en kö asynkront:
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
Så här skickar du en uppgift till en kö synkront:
dispatch_sync(queue) {
// Your sync code
}
// Code after the sync block will wait until the sync task finished
För att skicka en uppgift till efter ett tidsintervall (använd NSEC_PER_SEC
att konvertera sekunder till nanosekunder):
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
}
Så här utför du en uppgift asynkront och uppdaterar gränssnittet:
dispatch_async(queue) {
// Your time consuming code here
dispatch_async(dispatch_get_main_queue()) {
// Update the UI code
}
}
OBS: Eventuella uppdateringar av användargränssnittet ska anropas på huvudtråden! Se till att du lägger in koden för UI-uppdateringar i
dispatch_async(dispatch_get_main_queue()) { ... }
Samtidiga öglor
GCD tillhandahåller mekanism för att utföra en slinga, varvid slingorna sker samtidigt med avseende på varandra. Detta är mycket användbart när du gör en serie beräkningar dyra.
Tänk på den här slingan:
for index in 0 ..< iterations {
// Do something computationally expensive here
}
Du kan utföra dessa beräkningar samtidigt med concurrentPerform
(i Swift 3) eller dispatch_apply
(i Swift 2):
DispatchQueue.concurrentPerform(iterations: iterations) { index in
// Do something computationally expensive here
}
dispatch_apply(iterations, queue) { index in
// Do something computationally expensive here
}
Loop-stängningen kommer att påkallas för varje index
från 0
till, men inte inkluderande, iterations
. Dessa iterationer kommer att köras samtidigt med avseende på varandra, och därmed garanteras inte ordningen att de körs. Det faktiska antalet iterationer som sker samtidigt vid en viss tidpunkt bestäms vanligtvis av kapaciteten hos den aktuella enheten (t.ex. hur många kärnor har enheten).
Ett par speciella överväganden:
concurrentPerform
/dispatch_apply
kan köra slingorna samtidigt med avseende på varandra, men allt detta sker synkront med avseende på tråden från vilken du kallar det. Så ring inte detta från huvudtråden, eftersom det kommer att blockera den tråden tills slingan är klar.Eftersom dessa slingor inträffar samtidigt i förhållande till varandra, är du ansvarig för att säkerställa gängsäkerheten för resultaten. Om du till exempel uppdaterar en viss ordlista med resultaten från dessa beräknings dyra beräkningar, se till att du synkroniserar dessa uppdateringar själv.
Observera att det finns en del omkostnader för att köra samtidiga slingor. Om beräkningarna som utförs inuti slingan inte är tillräckligt beräkningsintensiva, kan du upptäcka att alla prestanda som uppnåtts med användning av samtidiga slingor kan minskas, om inte helt kompenseras, av omkostnaderna för att synkronisera alla dessa samtidiga trådar.
Så du ansvarar för att bestämma rätt mängd arbete som ska utföras i varje iteration av slingan. Om beräkningarna är för enkla kan du använda "steg" för att inkludera mer arbete per slinga. Till exempel, istället för att göra en samtidig loop med 1 miljon triviala beräkningar, kan du göra 100 iterationer i din slinga och göra 10 000 beräkningar per slinga. På det sättet är det tillräckligt med arbete som utförs på varje tråd, så att omkostnaderna för att hantera dessa samtidiga slingor blir mindre betydande.
Köra uppgifter i en OperationQueue
Du kan tänka på en OperationQueue
som en rad uppgifter som väntar på att utföras. Till skillnad från avsändningsköer i GCD är operationsköerna inte FIFO (först-in-först-ut). Istället utför de uppgifter så snart de är redo att köras, så länge det finns tillräckligt med systemresurser för att tillåta det.
Få den viktigaste OperationQueue
:
let mainQueue = OperationQueue.main
Skapa en anpassad OperationQueue
:
let queue = OperationQueue()
queue.name = "My Queue"
queue.qualityOfService = .default
Service of Quality specificerar vikten av arbetet, eller hur mycket användaren troligtvis räknar med omedelbara resultat från uppgiften.
Lägg till en Operation
till en OperationQueue
:
// An instance of some Operation subclass
let operation = BlockOperation {
// perform task here
}
queue.addOperation(operation)
Lägg till ett block i en OperationQueue
:
myQueue.addOperation {
// some task
}
Lägg till flera Operation
i en OperationQueue
:
let operations = [Operation]()
// Fill array with Operations
myQueue.addOperation(operations)
Justera hur många Operation
kan köras samtidigt i kön:
myQueue.maxConcurrentOperationCount = 3 // 3 operations may execute at once
// Sets number of concurrent operations based on current system conditions
myQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
Att avbryta en kö förhindrar att den startar exekveringen av befintliga, ostartade operationer eller av några nya åtgärder som läggs till i kön. Sättet att återuppta kön är att ställa in isSuspended
tillbaka till false
:
myQueue.isSuspended = true
// Re-enable execution
myQueue.isSuspended = false
Att avbryta en OperationQueue
stoppar eller avbryter inte operationer som redan körs. Man bör bara försöka stänga av en kö som du skapade, inte globala köer eller huvudkön.
Skapa operationer på hög nivå
Grundramen tillhandahåller Operation
, som representerar ett objekt på hög nivå som kapslar in en del av arbetet som kan utföras i en kö. Kön koordinerar inte bara prestandan för dessa operationer, utan du kan också upprätta beroenden mellan operationer, skapa avbrytbara operationer, begränsa graden av samtidighet som används i operationskön, etc.
Operation
blir redo att utföra när alla beroenden har slutförts. isReady
ändras sedan till true
.
Skapa en enkel underklass för icke-samtidiga Operation
:
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
}
}
Lägg till en operation i en OperationQueue
:
myQueue.addOperation(operation)
Detta kommer att utföra operationen samtidigt i kön.
Hantera beroenden av en Operation
.
Beroenden definierar andra Operation
som måste köras i en kö innan den Operation
anses redo att genomföras.
operation2.addDependency(operation1)
operation2.removeDependency(operation1)
Kör en Operation
utan kö:
operation.start()
Beroende kommer att ignoreras. Om detta är en samtidig operation, kan uppgiften fortfarande utföras samtidigt om dess start
metod avlastar arbeta för att bakgrunds köer.
Samtidig verksamhet.
Om uppgiften som en Operation
ska utföra är själva asynkron (t.ex. en URLSession
), bör du implementera Operation
som en samtidig operation. I det här fallet din isAsynchronous
bör genomförandet returnera true
, skulle du i allmänhet start
metod som utför en viss installation, anropar sedan dess main
metod som faktiskt utför uppgiften.
När implementeringen av en asynkron Operation
börjar måste du implementera isExecuting
, isFinished
metoder och KVO. Så när exekveringen startar, isExecuting
egendom till true
. När en Operation
slutför sin uppgift är isExecuting
inställt på false
och isFinished
är satt till true
. Om operationen avbryts är både isCancelled
och isFinished
ändras till true
. Alla dessa egenskaper kan observeras nyckelvärde.
Avbryt en Operation
.
cancel
ändrar helt isCancelled
egenskapen isCancelled
till true
. För att svara på avbokning från din egen Operation
underklass, bör du kontrollera värdet på isCancelled
åtminstone regelbundet inom main
och svara på lämpligt sätt.
operation.cancel()