grand gcd dispatchqueue dispatch_async central async ios multithreading concurrency grand-central-dispatch

ios - gcd - grand central dispatch swift



Colas concurrentes vs serie en GCD (3)

Estoy luchando por comprender completamente las colas concurrentes y en serie en GCD. Tengo algunos problemas y espero que alguien me pueda responder claramente y en el momento.

  1. Estoy leyendo que las colas en serie se crean y se usan para ejecutar las tareas una después de la otra. Sin embargo, ¿qué ocurre si:

    • Creo una cola en serie
    • Utilizo dispatch_async (en la cola serie que acabo de crear) tres veces para despachar tres bloques A, B, C

    ¿Se ejecutarán los tres bloques?

    • en orden A, B, C porque la cola es serial

      O

    • concurrentemente (al mismo tiempo en hilos parralel) porque utilicé el envío ASYNC
  2. Estoy leyendo que puedo usar dispatch_sync en colas concurrentes para ejecutar bloques uno después del otro. En ese caso, ¿POR QUÉ existen colas en serie, ya que siempre puedo usar una cola concurrente donde puedo enviar SYNCHRONOUSLY todos los bloques que quiera?

    Gracias por cualquier buena explicación!


Aquí hay un par de experimentos que he hecho para hacerme entender sobre estas colas concurrent y en serial con Grand Central Dispatch .

func doLongAsyncTaskInSerialQueue() { let serialQueue = DispatchQueue(label: "com.queue.Serial") for i in 1...5 { serialQueue.async { if Thread.isMainThread{ print("task running in main thread") }else{ print("task running in background thread") } let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")! let _ = try! Data(contentsOf: imgURL) print("/(i) completed downloading") } } }

La tarea se ejecutará en un hilo diferente (que no sea el hilo principal) cuando use async en GCD. Async significa ejecutar la siguiente línea, no espere hasta que se ejecute el bloque, lo que no bloquea el hilo principal y la cola principal. Desde su cola en serie, todos se ejecutan en el orden en que se agregan a la cola serie. Las tareas ejecutadas en serie siempre se ejecutan de a una por el único hilo asociado con la cola.

func doLongSyncTaskInSerialQueue() { let serialQueue = DispatchQueue(label: "com.queue.Serial") for i in 1...5 { serialQueue.sync { if Thread.isMainThread{ print("task running in main thread") }else{ print("task running in background thread") } let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")! let _ = try! Data(contentsOf: imgURL) print("/(i) completed downloading") } } }

La tarea puede ejecutarse en el hilo principal cuando usa sincronización en GCD. Sync ejecuta un bloque en una cola determinada y espera que se complete, lo que resulta en el bloqueo del hilo principal o la cola principal. Dado que la cola principal debe esperar hasta que el bloque enviado finalice, el hilo principal estará disponible para procesar bloques de colas que no sean cola principal. Por lo tanto, existe la posibilidad de que el código que se ejecuta en la cola de fondo se esté ejecutando en el hilo principal. Desde su cola serie, todos se ejecutan en el orden en que se agregan (FIFO).

func doLongASyncTaskInConcurrentQueue() { let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent) for i in 1...5 { concurrentQueue.async { if Thread.isMainThread{ print("task running in main thread") }else{ print("task running in background thread") } let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")! let _ = try! Data(contentsOf: imgURL) print("/(i) completed downloading") } print("/(i) executing") } }

La tarea se ejecutará en el hilo de fondo cuando use async en GCD. Async significa ejecutar la siguiente línea, no espere hasta que se ejecute el bloque, lo que resulta en un bloqueo del hilo principal. Recuerde que en la cola concurrente, las tareas se procesan en el orden en que se agregan a la cola, pero con diferentes subprocesos conectados a la cola. Recuerde que no se supone que terminen la tarea en el orden en que se agregan a la cola. El orden de la tarea varía cada vez que los hilos se crean como necesariamente automáticamente. Las tareas se ejecutan en paralelo. Con más de eso (maxConcurrentOperationCount) se alcanza, algunas tareas se comportarán como una serie hasta que un hilo sea libre.

func doLongSyncTaskInConcurrentQueue() { let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent) for i in 1...5 { concurrentQueue.sync { if Thread.isMainThread{ print("task running in main thread") }else{ print("task running in background thread") } let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")! let _ = try! Data(contentsOf: imgURL) print("/(i) completed downloading") } print("/(i) executed") } }

La tarea puede ejecutarse en el hilo principal cuando usa sincronización en GCD. Sync ejecuta un bloque en una cola determinada y espera que se complete, lo que resulta en el bloqueo del hilo principal o la cola principal. Dado que la cola principal debe esperar hasta que el bloque enviado finalice, el hilo principal estará disponible para procesar bloques de colas que no sean cola principal. Por lo tanto, existe la posibilidad de que el código que se está ejecutando en la cola de fondo realmente se esté ejecutando en el hilo principal. Debido a que su cola es concurrente, las tareas pueden no finalizar en el orden en que se agregan a la cola. Pero con la operación sincrónica sí lo hace, aunque pueden ser procesados ​​por diferentes hilos. Entonces, se comporta como esta es la cola serial.

Aquí hay un resumen de estos experimentos

Recuerde usar GCD, solo está agregando tareas a la cola y realizando tareas desde esa cola. Cola envía su tarea en el hilo principal o en el fondo dependiendo de si la operación es síncrona o asíncrona. Los tipos de colas son en serie, concurrentes, cola de despacho principal. Toda la tarea que realiza se realiza de forma predeterminada desde la cola de despacho principal. Ya hay cuatro colas simultáneas globales predefinidas para su aplicación y una cola principal (DispatchQueue.main). Usted también puede crear manualmente su propia cola y realizar tareas desde esa cola.

La tarea relacionada con la interfaz de usuario se debe realizar siempre desde el hilo principal enviando la tarea a la cola principal. La utilidad de la mano corta es DispatchQueue.main.sync/async mientras que las operaciones relacionadas / pesadas de red siempre se deben realizar de forma asincrónica, independientemente del hilo que utilice. o fondo

EDITAR: Sin embargo, hay casos en los que necesita realizar operaciones de llamadas de red de forma síncrona en un hilo de fondo sin congelar la IU (por ejemplo, restaurar el Token OAuth y esperar si tiene éxito o no). Debe ajustar ese método dentro de una operación asincrónica. las operaciones se ejecutan en el orden y sin bloquear el hilo principal.

func doMultipleSyncTaskWithinAsynchronousOperation() { let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent) concurrentQueue.async { let concurrentQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default) for i in 1...5 { concurrentQueue.sync { let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")! let _ = try! Data(contentsOf: imgURL) print("/(i) completed downloading") } print("/(i) executed") } } }

EDITAR EDITAR: Puedes ver un video de demostración here


Si entiendo correctamente cómo funciona GCD, creo que hay dos tipos de DispatchQueue , serial y concurrent , al mismo tiempo, hay dos formas en que DispatchQueue despacha sus tareas, el closure asignado, el primero es async y el otro es sync Aquellos juntos determinan cómo se ejecuta realmente el cierre (tarea).

Encontré que serial y concurrent significan cuántos hilos puede usar esa cola, serial significa uno, mientras que concurrent significa muchos. Y sync y async significan que la tarea se ejecutará en qué subproceso, el subproceso de la persona que llama o el subproceso subyacente a esa cola, la sync significa ejecutar en el subproceso mientras que async significa ejecutar en el subproceso subyacente.

El siguiente es un código experimental que se puede ejecutar en Xcode playground.

PlaygroundPage.current.needsIndefiniteExecution = true let cq = DispatchQueue(label: "concurrent.queue", attributes: .concurrent) let cq2 = DispatchQueue(label: "concurent.queue2", attributes: .concurrent) let sq = DispatchQueue(label: "serial.queue") func codeFragment() { print("code Fragment begin") print("Task Thread:/(Thread.current.description)") let imgURL = URL(string: "http://.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground")! let _ = try! Data(contentsOf: imgURL) print("code Fragment completed") } func serialQueueSync() { sq.sync { codeFragment() } } func serialQueueAsync() { sq.async { codeFragment() } } func concurrentQueueSync() { cq2.sync { codeFragment() } } func concurrentQueueAsync() { cq2.async { codeFragment() } } func tasksExecution() { (1...5).forEach { (_) in /// Using an concurrent queue to simulate concurent task executions. cq.async { print("Caller Thread:/(Thread.current.description)") /// Serial Queue Async, tasks run serially, because only one thread that can be used by serial queue, the underlying thread of serial queue. //serialQueueAsync() /// Serial Queue Sync, tasks run serially, because only one thread that can be used by serial queue,one by one of the callers'' threads. //serialQueueSync() /// Concurrent Queue Async, tasks run concurrently, because tasks can run on different underlying threads //concurrentQueueAsync() /// Concurrent Queue Sync, tasks run concurrently, because tasks can run on different callers'' thread //concurrentQueueSync() } } } tasksExecution()

Espero que pueda ser útil.


Un ejemplo simple: tienes un bloque que tarda un minuto en ejecutarse. Usted lo agrega a una cola del hilo principal. Veamos los cuatro casos.

  • async - concurrente: el código se ejecuta en un hilo de fondo. El control vuelve inmediatamente al hilo principal (y a la IU). El bloque no puede suponer que es el único bloque que se ejecuta en esa cola
  • async - serial: el código se ejecuta en una cadena de fondo. El control vuelve inmediatamente al hilo principal. El bloque puede asumir que es el único bloque que se ejecuta en esa cola
  • sincronización: concurrente: el código se ejecuta en un subproceso en segundo plano, pero el subproceso principal espera a que finalice, bloqueando las actualizaciones de la interfaz de usuario. El bloque no puede suponer que es el único bloque que se ejecuta en esa cola (podría haber agregado otro bloque usando async unos segundos antes)
  • sync - serial: el código se ejecuta en un subproceso en segundo plano, pero el subproceso principal espera a que termine, bloqueando las actualizaciones de la interfaz de usuario. El bloque puede asumir que es el único bloque que se ejecuta en esa cola

Obviamente, no usaría ninguno de los dos últimos para procesos de larga ejecución. Normalmente lo ve cuando intenta actualizar la interfaz de usuario (siempre en el hilo principal) de algo que puede estar ejecutándose en otro hilo.