Swift Language
Concorrenza
Ricerca…
Sintassi
Swift 3.0
DispatchQueue.main // Ottieni la coda principale
DispatchQueue (label: "my-serial-queue", attributi: [.serial, .qosBackground]) // Crea la tua coda seriale privata
DispatchQueue.global (attributi: [.qosDefault]) // Accede a una delle code simultanee globali
DispatchQueue.main.async {...} // Invia un'attività in modo asincrono al thread principale
DispatchQueue.main.sync {...} // Invia un'attività in modo sincrono al thread principale
DispatchQueue.main.asyncAfter (deadline: .now () + 3) {...} // Invio di un'attività in modo asincrono al thread principale da eseguire dopo x secondi
Swift <3.0
dispatch_get_main_queue () // Ottieni la coda principale in esecuzione sul thread principale
dispatch_get_global_queue (dispatch_queue_priority_t, 0) // Ottieni la coda globale con priorità specificata dispatch_queue_priority_t
dispatch_async (dispatch_queue_t) {() -> Void in ...} // Invio di un'attività in modo asincrono sul dispatch_queue_t specificato
dispatch_sync (dispatch_queue_t) {() -> Void in ...} // Invio di un'attività in modo sincrono sulla dispatch_queue_t specificata
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, Int64 (nanoseconds)), dispatch_queue_t, {...}); // Invio di un'attività su dispatch_queue_t specificato dopo nanosecondi
Ottenere una coda Grand Central Dispatch (GCD)
Grand Central Dispatch lavora sul concetto di "Dispatch Queues". Una coda di invio esegue le attività designate nell'ordine in cui sono state inoltrate. Esistono tre tipi di code di invio:
- Le code di invio seriale (ovvero le code di invio private) eseguono un compito alla volta, in ordine. Sono spesso utilizzati per sincronizzare l'accesso a una risorsa.
- Le code simultanee di invio (ovvero le code di invio globali) eseguono contemporaneamente una o più attività.
- La Main Dispatch Queue esegue le attività sul thread principale.
Per accedere alla coda principale:
let mainQueue = DispatchQueue.main
let mainQueue = dispatch_get_main_queue()
Il sistema fornisce code di invio globali simultanee (globali all'applicazione), con priorità diverse. Puoi accedere a queste code usando la classe DispatchQueue
in Swift 3:
let globalConcurrentQueue = DispatchQueue.global(qos: .default)
equivalente a
let globalConcurrentQueue = DispatchQueue.global()
let globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
In iOS 8 o versioni successive, la possibile qualità dei valori di servizio che possono essere passati sono .userInteractive
, .userInitiated
, .default
, .utility
e .background
. Sostituiscono le costanti DISPATCH_QUEUE_PRIORITY_
.
Puoi anche creare le tue code con priorità diverse:
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, le code create con questo inizializzatore sono di serie per impostazione predefinita e il passaggio .workItem
per la frequenza di autorelease garantisce che un pool di autorelease venga creato e scaricato per ciascun elemento di lavoro. C'è anche. .never
, il che significa che gestirai i tuoi pool di autorilease autonomamente, o. .inherit
che eredita l'impostazione dall'ambiente. Nella maggior parte dei casi probabilmente non lo userai .never
tranne nei casi di estrema personalizzazione.
Esecuzione di attività in una coda Grand Central Dispatch (GCD)
Per eseguire attività su una coda di invio, utilizzare i metodi sync
, async
e after
.
Per inviare un'attività a una coda in modo asincrono:
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
Per inviare un'attività a una coda in modo sincrono:
queue.sync {
// Do some task
}
// ... code here will not execute until the task is finished
Per inviare un'attività in una coda dopo un determinato numero di secondi:
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
NOTA: Eventuali aggiornamenti dell'interfaccia utente dovrebbero essere richiamati sul thread principale! Assicurati di inserire il codice per gli aggiornamenti dell'interfaccia utente all'interno di
DispatchQueue.main.async { ... }
Tipi di coda:
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)
Per inviare un'attività a una coda in modo asincrono:
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
Per inviare un'attività a una coda in modo sincrono:
dispatch_sync(queue) {
// Your sync code
}
// Code after the sync block will wait until the sync task finished
Per inviare un'attività dopo un intervallo di tempo (utilizzare NSEC_PER_SEC
per convertire secondi in nanosecondi):
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
}
Per eseguire un'attività in modo asincrono e aggiornare l'interfaccia utente:
dispatch_async(queue) {
// Your time consuming code here
dispatch_async(dispatch_get_main_queue()) {
// Update the UI code
}
}
NOTA: Eventuali aggiornamenti dell'interfaccia utente dovrebbero essere richiamati sul thread principale! Assicurati di aver inserito il codice per gli aggiornamenti dell'interfaccia utente all'interno di
dispatch_async(dispatch_get_main_queue()) { ... }
Cicli concomitanti
GCD fornisce un meccanismo per eseguire un ciclo, in cui i cicli si verificano contemporaneamente l'uno rispetto all'altro. Questo è molto utile quando si eseguono una serie di calcoli computazionalmente costosi.
Considera questo ciclo:
for index in 0 ..< iterations {
// Do something computationally expensive here
}
È possibile eseguire questi calcoli contemporaneamente utilizzando concurrentPerform
(in Swift 3) o dispatch_apply
(in Swift 2):
DispatchQueue.concurrentPerform(iterations: iterations) { index in
// Do something computationally expensive here
}
dispatch_apply(iterations, queue) { index in
// Do something computationally expensive here
}
La chiusura del ciclo sarà invocata per ogni index
da 0
a, ma non incluse, iterations
. Queste iterazioni verranno eseguite simultaneamente l'una rispetto all'altra e quindi l'ordine che esse eseguono non è garantito. Il numero effettivo di iterazioni che si verificano simultaneamente in un dato momento è generalmente dettato dalle capacità del dispositivo in questione (ad esempio quanti core ha il dispositivo).
Un paio di considerazioni speciali:
Il
concurrentPerform
/dispatch_apply
può eseguire i loop simultaneamente l'uno rispetto all'altro, ma tutto avviene in modo sincrono rispetto al thread da cui viene chiamato. Quindi, non chiamarlo dal thread principale, in quanto ciò bloccherà quel thread fino a quando il ciclo non sarà terminato.Poiché questi cicli si verificano contemporaneamente l'uno rispetto all'altro, si è responsabili di garantire la sicurezza del thread dei risultati. Ad esempio, se si aggiornano alcuni dizionari con i risultati di questi calcoli computazionalmente costosi, assicurarsi di sincronizzarli autonomamente.
Nota, c'è un sovraccarico associato nell'esecuzione di cicli concomitanti. Pertanto, se i calcoli eseguiti all'interno del ciclo non sono sufficientemente intensi dal punto di vista computazionale, è possibile che le prestazioni ottenute utilizzando loop concomitanti possano essere diminuite, se non completamente compensate, dall'overhead associato alla sincronizzazione di tutti questi thread simultanei.
Quindi, sei responsabile della determinazione della quantità corretta di lavoro da eseguire in ogni iterazione del ciclo. Se i calcoli sono troppo semplici, puoi usare "striding" per includere più work per loop. Ad esempio, anziché eseguire un ciclo simultaneo con 1 milione di calcoli triviali, è possibile eseguire 100 iterazioni nel ciclo, eseguendo 10.000 calcoli per ciclo. In questo modo viene eseguito abbastanza lavoro su ciascun thread, quindi l'overhead associato alla gestione di questi loop simultanei diventa meno significativo.
Esecuzione di attività in un'operazioneQueue
Puoi pensare a OperationQueue
come a una linea di compiti in attesa di essere eseguita. A differenza delle code di invio in GCD, le code di operazioni non sono FIFO (first-in-first-out). Invece, eseguono compiti non appena sono pronti per essere eseguiti, a patto che ci siano abbastanza risorse di sistema per consentirne l'esecuzione.
Ottieni l' OperationQueue
principale:
let mainQueue = OperationQueue.main
Creare un OperationQueue
personalizzato:
let queue = OperationQueue()
queue.name = "My Queue"
queue.qualityOfService = .default
Quality of Service specifica l'importanza del lavoro o quanto l'utente è probabile che contenga risultati immediati dall'attività.
Aggiungere un Operation
ad un OperationQueue
:
// An instance of some Operation subclass
let operation = BlockOperation {
// perform task here
}
queue.addOperation(operation)
Aggiungi un blocco a OperationQueue
:
myQueue.addOperation {
// some task
}
Aggiungere più Operation
s a OperationQueue
:
let operations = [Operation]()
// Fill array with Operations
myQueue.addOperation(operations)
Regola il numero di Operation
che possono essere eseguite contemporaneamente all'interno della coda:
myQueue.maxConcurrentOperationCount = 3 // 3 operations may execute at once
// Sets number of concurrent operations based on current system conditions
myQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
La sospensione di una coda impedirà l'avvio dell'esecuzione di qualsiasi operazione esistente, non avviata o di qualsiasi nuova operazione aggiunta alla coda. Il modo per riprendere questa coda è di impostare isSuspended
su false
:
myQueue.isSuspended = true
// Re-enable execution
myQueue.isSuspended = false
Sospensione di OperationQueue
non si ferma o annullare operazioni che sono già in esecuzione. Si dovrebbe tentare solo di sospendere una coda che è stata creata, non le code globali o la coda principale.
Creazione di operazioni di alto livello
Il framework Foundation fornisce il tipo di Operation
, che rappresenta un oggetto di alto livello che incapsula una porzione di lavoro che può essere eseguita su una coda. La coda non solo coordina le prestazioni di tali operazioni, ma è anche possibile stabilire dipendenze tra operazioni, creare operazioni cancellabili, limitare il grado di concorrenza utilizzato dalla coda di operazioni, ecc.
Operation
s sono pronte per essere eseguite al termine dell'esecuzione di tutte le sue dipendenze. La proprietà isReady
cambia in true
.
Creare una sottoclasse Operation
non simultanea semplice:
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
}
}
Aggiungi un'operazione a un OperationQueue
:
myQueue.addOperation(operation)
Questo eseguirà l'operazione contemporaneamente in coda.
Gestire le dipendenze su un Operation
.
Le dipendenze definiscono altre Operation
che devono essere eseguite su una coda prima che l' Operation
sia considerata pronta per l'esecuzione.
operation2.addDependency(operation1)
operation2.removeDependency(operation1)
Eseguire un Operation
senza una coda:
operation.start()
Le dipendenze saranno ignorate. Se si tratta di un'operazione simultanea, l'attività può ancora essere eseguita contemporaneamente se il suo metodo di start
scarica da lavoro alle code in background.
Operazioni simultanee.
Se il compito che l' Operation
è da eseguire è, a sua volta, asincrono, (ad esempio un URLSession
compito dati), è necessario implementare l' Operation
come un'operazione simultanea. In questo caso, l'implementazione isAsynchronous
deve restituire true
, in genere è necessario start
metodo che esegue alcune impostazioni, quindi chiama il suo metodo main
che esegue effettivamente l'attività.
Quando si implementa un asincrono Operation
inizia è necessario implementare isExecuting
, isFinished
metodi e KVO. Pertanto, quando viene avviata l'esecuzione, isExecuting
property cambia in true
. Quando un Operation
termina il suo compito, isExecuting
è impostato su false
, e isFinished
è impostata su true
. Se l'operazione è annullata, isCancelled
e isFinished
cambiano in true
. Tutte queste proprietà sono osservabili come valori chiave.
Annullamento di un Operation
.
Chiamando cancel
cambia semplicemente la proprietà isCancelled
su true
. Per rispondere alla cancellazione dalla propria sottoclasse Operation
, è necessario verificare il valore di isCancelled
almeno periodicamente all'interno di main
e rispondere in modo appropriato.
operation.cancel()