iOS
GCD (Grand Central Dispatch)
Buscar..
Introducción
Grand Central Dispatch (GCD) es la respuesta de Apple al multihilo. Es un marco liviano para realizar tareas de forma síncrona o asíncrona en colas y maneja los hilos de la CPU para usted entre bastidores.
Tema relacionado: Concurrencia
Crear una cola de envío
Puedes crear tu propia cola usando dispatch_queue_create
C objetivo
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
Rápido
// 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
Conseguir la cola principal
La cola principal es la cola de envío en la que tienen lugar todas las actualizaciones de la interfaz de usuario y se coloca el código que implica los cambios de la interfaz de usuario.
NSURLSession
llegar a la cola principal para actualizar la interfaz de usuario al finalizar un proceso asíncrono como NSURLSession
Hay dos tipos de llamadas de cola principales synchronous
y asynchronous
. Cuando invocas algo de forma synchronously
, significa que el hilo que inició esa operación esperará a que la tarea termine antes de continuar. Asynchronous
significa que no esperará.
Código Objetivo-C
Llamada de cola principal Synchronous
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Llamada Asynchronous
la cola principal
dispatch_async(dispatch_get_main_queue(), ^{
// do work here to Usually to update the User Interface
});
SWIFT 3
Llamada Asynchronous
la cola principal
DispatchQueue.main.async {
}
Llamada de cola principal Synchronous
DispatchQueue.main.sync {
}
Grupo de despacho
DispatchGroup permite la sincronización agregada del trabajo. Puede usarlos para enviar varios elementos de trabajo diferentes y realizar un seguimiento cuando se completen, incluso aunque se ejecuten en diferentes colas. Este comportamiento puede ser útil cuando no se puede avanzar hasta completar todas las tareas especificadas.
Un escenario en el que esto podría ser útil es si tiene varias llamadas de servicio web que todas necesitan terminar antes de continuar. Por ejemplo, necesita descargar varios conjuntos de datos que deben ser procesados por alguna función. Debe esperar a que todos los servicios web se completen antes de llamar a la función para procesar todos los datos recibidos.
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!")
}
Alternativamente, si no desea esperar a que finalicen los grupos, sino que desea ejecutar una función una vez que todas las tareas hayan finalizado, use la función de notify
en lugar de 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
}
Ejemplo de salida:
starting long running tasks
long task 0 done!
long task 3 done!
long task 1 done!
long task 2 done!
all tasks done!
Para obtener más información, consulte los documentos de Apple o el tema relacionado
Despido semáforo
DispatchSemaphore proporciona una implementación eficiente de un semáforo de conteo tradicional, que se puede usar para controlar el acceso a un recurso en múltiples contextos de ejecución.
Un escenario para cuándo usar un semáforo podría ser si está leyendo o escribiendo un archivo, si varias tareas intentan leer y escribir desde un archivo al mismo tiempo, podría aumentar su rendimiento para hacer que cada tarea espere su turno para que Para no sobrecargar el controlador de E / S.
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
}
}
}
Ejemplo de salida: (observe las marcas de tiempo)
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
Para más información, consulte los documentos de Apple.
Serial vs Colas de despacho concurrentes
Swift 3
Cola de serie
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)")
}
}
}
Ejemplo de salida:
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
Cola concurrente
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)")
}
}
}
Ejemplo de salida:
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
Discusión
Como podemos ver en los ejemplos anteriores, una cola en serie completará cada tarea en el orden en que se envían a la cola. Cada tarea esperará a que la tarea anterior termine antes de ejecutarse. En cuanto a la cola concurrente, cada tarea no espera a las otras en la cola y se ejecuta tan pronto como sea posible; La ventaja es que todas las tareas en la cola se ejecutarán al mismo tiempo en subprocesos separados, haciendo que una cola concurrente tome menos tiempo que una cola en serie.
Si el orden de ejecución de las tareas no es importante, siempre use una cola concurrente para la mejor eficiencia.