Swift Language
Concurrencia
Buscar..
Sintaxis
Swift 3.0
DispatchQueue.main // Obtener la cola principal
DispatchQueue (etiqueta: "my-serial-queue", atributos: [.serial, .qosBackground]) // Crea tu propia cola serial privada
DispatchQueue.global (atributos: [.qosDefault]) // Accede a una de las colas simultáneas globales
DispatchQueue.main.async {...} // Distribuye una tarea de forma asíncrona al hilo principal
DispatchQueue.main.sync {...} // Distribuye una tarea de forma sincrónica al hilo principal
DispatchQueue.main.asyncAfter (fecha límite: .now () + 3) {...} // Distribuye una tarea de forma asíncrona al hilo principal que se ejecutará después de x segundos
Swift <3.0
dispatch_get_main_queue () // Obtenga la cola principal ejecutándose en el hilo principal
dispatch_get_global_queue (dispatch_queue_priority_t, 0) // Obtener cola global con prioridad especificada dispatch_queue_priority_t
dispatch_async (dispatch_queue_t) {() -> Void in ...} // Distribuye una tarea de forma asíncrona en la dispatch_queue_t especificada
dispatch_sync (dispatch_queue_t) {() -> Void in ...} // Distribuye una tarea de forma síncrona en el dispatch_queue_t especificado
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, Int64 (nanosegundos)), dispatch_queue_t, {...}); // Enviar una tarea en la dispatch_queue_t especificada después de nanosegundos
Obtención de una cola de Grand Central Dispatch (GCD)
Grand Central Dispatch trabaja en el concepto de "Dispatch Queues". Una cola de envío ejecuta las tareas que usted designa en el orden en que se pasan. Hay tres tipos de colas de despacho:
- Serial Dispatch Queues (colas de envío privado) se ejecutan una tarea a la vez, en orden. Se utilizan con frecuencia para sincronizar el acceso a un recurso.
- Las colas de despacho simultáneas (también conocidas como colas de despacho globales) ejecutan una o más tareas simultáneamente.
- La cola de envío principal ejecuta tareas en el hilo principal.
Para acceder a la cola principal:
let mainQueue = DispatchQueue.main
let mainQueue = dispatch_get_main_queue()
El sistema proporciona colas de despacho globales simultáneas (globales a su aplicación), con diferentes prioridades. Puede acceder a estas colas utilizando la clase DispatchQueue
en 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)
En iOS 8 o posterior, los posibles valores de calidad de servicio que se pueden pasar son .userInteractive
, .userInitiated
, .default
, .utility
y .background
. Estos reemplazan las constantes DISPATCH_QUEUE_PRIORITY_
.
También puede crear sus propias colas con diferentes prioridades:
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)
En Swift 3, las colas creadas con este inicializador son en serie de forma predeterminada, y pasar .workItem
para la frecuencia de liberación automática garantiza que se crea y se vacía un grupo de autorelease para cada elemento de trabajo. También existe .never
, lo que significa que usted mismo administrará sus propios grupos de autorelease, o .inherit
que hereda la configuración del entorno. En la mayoría de los casos, es probable que no use .never
excepto en casos de personalización extrema.
Ejecución de tareas en una cola de Grand Central Dispatch (GCD)
Para ejecutar tareas en una cola de envío, use los métodos sync
, async
y after
.
Para enviar una tarea a una cola de forma asíncrona:
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
Para enviar una tarea a una cola de forma síncrona:
queue.sync {
// Do some task
}
// ... code here will not execute until the task is finished
Para enviar una tarea a una cola después de un cierto número de segundos:
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: ¡ Cualquier actualización de la interfaz de usuario debe llamarse en el hilo principal! Asegúrese de que coloca el código para las actualizaciones de la interfaz de usuario dentro de
DispatchQueue.main.async { ... }
Tipos de cola:
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)
Para enviar una tarea a una cola de forma asíncrona:
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
Para enviar una tarea a una cola de forma síncrona:
dispatch_sync(queue) {
// Your sync code
}
// Code after the sync block will wait until the sync task finished
Para enviar una tarea después de un intervalo de tiempo (use NSEC_PER_SEC
para convertir segundos a nanosegundos):
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
}
Para ejecutar una tarea de forma asíncrona y luego actualizar la interfaz de usuario:
dispatch_async(queue) {
// Your time consuming code here
dispatch_async(dispatch_get_main_queue()) {
// Update the UI code
}
}
NOTA: ¡ Cualquier actualización de la interfaz de usuario debe llamarse en el hilo principal! Asegúrese de que coloca el código para las actualizaciones de la interfaz de usuario dentro de
dispatch_async(dispatch_get_main_queue()) { ... }
Loops concurrentes
GCD proporciona un mecanismo para realizar un bucle, por lo que los bucles se producen simultáneamente entre sí. Esto es muy útil cuando se realizan una serie de cálculos computacionalmente costosos.
Considere este bucle:
for index in 0 ..< iterations {
// Do something computationally expensive here
}
Puede realizar esos cálculos al mismo tiempo utilizando concurrentPerform
(en Swift 3) o dispatch_apply
(en Swift 2):
DispatchQueue.concurrentPerform(iterations: iterations) { index in
// Do something computationally expensive here
}
dispatch_apply(iterations, queue) { index in
// Do something computationally expensive here
}
El cierre del bucle se invocará para cada index
de 0
a, pero sin incluir, iterations
. Estas iteraciones se ejecutarán simultáneamente entre sí, y por lo tanto el orden en que se ejecutan no está garantizado. El número real de iteraciones que ocurren simultáneamente en un momento dado generalmente viene determinado por las capacidades del dispositivo en cuestión (por ejemplo, cuántos núcleos tiene el dispositivo).
Un par de consideraciones especiales:
concurrentPerform
/dispatch_apply
puede ejecutar los bucles simultáneamente entre sí, pero todo esto sucede de forma sincrónica con respecto al subproceso desde el que lo llama. Por lo tanto, no lo llames desde el hilo principal, ya que bloqueará ese hilo hasta que se complete el ciclo.Debido a que estos bucles se producen simultáneamente entre sí, usted es responsable de garantizar la seguridad de los resultados. Por ejemplo, si actualiza algún diccionario con los resultados de estos cálculos computacionalmente costosos, asegúrese de sincronizar esas actualizaciones usted mismo.
Tenga en cuenta que hay algunos gastos generales asociados con la ejecución de bucles simultáneos. Por lo tanto, si los cálculos que se realizan dentro del bucle no son lo suficientemente intensivos en computación, puede encontrar que cualquier rendimiento obtenido mediante el uso de bucles concurrentes puede disminuir, si no compensarse completamente, por la sobrecarga asociada con la sincronización de todos estos subprocesos concurrentes.
Por lo tanto, usted es responsable de determinar la cantidad correcta de trabajo a realizar en cada iteración del bucle. Si los cálculos son demasiado simples, puede emplear "zancada" para incluir más trabajo por bucle. Por ejemplo, en lugar de hacer un bucle concurrente con 1 millón de cálculos triviales, puede hacer 100 iteraciones en su bucle, haciendo 10.000 cálculos por bucle. De esa manera, se realiza suficiente trabajo en cada subproceso, por lo que la sobrecarga asociada con la administración de estos bucles simultáneos se vuelve menos significativa.
Ejecutar tareas en un OperationQueue
Puede pensar en un OperationQueue
como una línea de tareas que esperan ser ejecutadas. A diferencia de las colas de despacho en GCD, las colas de operación no son FIFO (primero en entrar, primero en salir). En su lugar, ejecutan tareas tan pronto como están listas para ejecutarse, siempre que haya suficientes recursos del sistema para permitirlo.
Obtener el principal OperationQueue
:
let mainQueue = OperationQueue.main
Crear un OperationQueue
personalizado:
let queue = OperationQueue()
queue.name = "My Queue"
queue.qualityOfService = .default
La calidad del servicio especifica la importancia del trabajo o la cantidad de probabilidades de que el usuario cuente con los resultados inmediatos de la tarea.
Agregar una Operation
a un OperationQueue
:
// An instance of some Operation subclass
let operation = BlockOperation {
// perform task here
}
queue.addOperation(operation)
Añadir un bloque a un OperationQueue
:
myQueue.addOperation {
// some task
}
Agregue múltiples Operation
a un OperationQueue
:
let operations = [Operation]()
// Fill array with Operations
myQueue.addOperation(operations)
Ajuste la cantidad de Operation
se pueden ejecutar simultáneamente en la cola:
myQueue.maxConcurrentOperationCount = 3 // 3 operations may execute at once
// Sets number of concurrent operations based on current system conditions
myQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
La suspensión de una cola evitará que comience la ejecución de cualquier operación existente, no iniciada o de cualquier nueva operación agregada a la cola. La forma de reanudar esta cola es establecer isSuspended
nuevo en false
:
myQueue.isSuspended = true
// Re-enable execution
myQueue.isSuspended = false
La suspensión de un OperationQueue
no detiene o cancela las operaciones que ya se están ejecutando. Uno solo debe intentar suspender una cola que usted creó, no las colas globales o la cola principal.
Creación de operaciones de alto nivel
Foundation Framework proporciona el tipo de Operation
, que representa un objeto de alto nivel que encapsula una parte del trabajo que puede ejecutarse en una cola. La cola no solo coordina el rendimiento de esas operaciones, sino que también puede establecer dependencias entre operaciones, crear operaciones cancelables, restringir el grado de concurrencia empleado por la cola de operaciones, etc.
Operation
están listas para ejecutarse cuando todas sus dependencias terminan de ejecutarse. La propiedad isReady
cambia a true
.
Cree una subclase de Operation
no concurrente simple:
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
}
}
Agregue una operación a un OperationQueue
:
myQueue.addOperation(operation)
Esto ejecutará la operación simultáneamente en la cola.
Gestionar dependencias en una Operation
.
Las dependencias definen otras Operation
que deben ejecutarse en una cola antes de que esa Operation
se considere lista para ejecutarse.
operation2.addDependency(operation1)
operation2.removeDependency(operation1)
Ejecutar una Operation
sin una cola:
operation.start()
Las dependencias serán ignoradas. Si se trata de una operación simultánea, la tarea aún puede ejecutarse simultáneamente si las descargas de su método de start
funcionan en colas en segundo plano.
Operaciones concurrentes.
Si la tarea que debe realizar una Operation
es, en sí misma, asíncrona, (por ejemplo, una tarea de datos URLSession
), debe implementar la Operation
como una operación concurrente. En este caso, su implementación isAsynchronous
debería devolver true
, generalmente tendría un método de start
que realiza alguna configuración y luego llama a su método main
que ejecuta la tarea.
Cuando se inicia la implementación de una Operation
asíncrona, debe implementar isExecuting
, isFinished
y KVO. Entonces, cuando se inicia la ejecución, isExecuting
propiedad cambia a true
. Cuando una Operation
finaliza su tarea, isExecuting
se establece en false
y isFinished
se establece en true
. Si la operación se cancela, se cancela tanto el isCancelled
como el cambio isFinished
en true
. Todas estas propiedades son clave-valor observable.
Cancelar una Operation
.
Llamar cancel
simplemente cambia la propiedad isCancelled
a true
. Para responder a la cancelación desde su propia subclase de Operation
, debe verificar el valor de isCancelled
al menos periódicamente dentro de main
y responder adecuadamente.
operation.cancel()