¿Cómo funciona la programación multiproceso en el desarrollo de iOS?

Introducción a la concurrencia de iOS

por Sergey Shabalin

Supongamos que tiene una aplicación móvil ejecutándose en el hilo principal que es responsable de la ejecución del código que manipula la interfaz de usuario. Cada vez que empaque su aplicación con piezas de código que consumen mucho tiempo, como la descarga desde la web o el procesamiento de imágenes en el hilo principal, esto ralentiza significativamente el rendimiento de la interfaz de usuario e incluso puede conducir a su congelación completa.

Multithreading en iOS

Entonces, ¿hay alguna manera de cambiar la arquitectura de la aplicación para que tales problemas nunca ocurran? Creo que la concurrencia puede ayudar aquí, ya que es capaz de ejecutar dos o más tareas independientes, como cálculos, descarga de datos desde la web o una unidad, procesamiento de imágenes, etc.

Sin embargo, este no es un viaje gratuito, con la introducción de la concurrencia, la seguridad del hilo de código podría verse comprometida en la ejecución. Tan pronto como permita que las tareas se ejecuten simultáneamente, esté preparado para problemas con las tareas que obtienen acceso a los mismos recursos, como cambiar la misma variable en diferentes subprocesos o acceder a los recursos ya bloqueados por otras tareas. En última instancia, esto podría conducir a la demolición de los recursos utilizados en diferentes hilos.

En el desarrollo de iOS, la concurrencia se usa para mejorar la productividad y la capacidad de respuesta de la interfaz de usuario, proporcionada por varias herramientas como Thread, GCD (Grand Central Dispatch) y Operation. Es seguro decir que antes de que surgiera Swift 3, el poderoso marco GCD estaba utilizando la API C, que era una maraña de oportunidades ocultas para las acciones de los usuarios. Swift 3 lo cambió todo. El GCD tiene una nueva sintaxis fácil de usar basada en la lógica del GCD.

Para comprender mejor cómo usar la concurrencia, descubramos qué nociones clave operan con las herramientas GCD y Operation. La noción básica es la cola . Entonces, cuando se habla de la concurrencia de iOS desde la perspectiva de un desarrollador, es muy probable que se mencionen las colas. Las colas tienen cierres alineados y de acuerdo con el orden especificado, el sistema los tira uno por uno y los despliega en los hilos apropiados. Las colas siguen el principio FIFO (primero en entrar, primero en salir) en múltiples hilos que constituyen el patrón de concurrencia.

Colas seriales contra colas concurrentes

Las colas pueden ser:

  • Serie o consecutiva, cuando iOS cierra el cierre en la parte superior de la cola y funciona hasta que termina, luego tira de otro elemento de la cola y así sucesivamente.

  • Concurrente o multiproceso, cuando el sistema tira un cierre en la parte superior de la cola e inicia su ejecución en un determinado hilo.

Si el sistema tiene acceso a más recursos, selecciona el siguiente elemento de la cola y lo inicia en un hilo diferente mientras la primera función aún está en funcionamiento. De esta manera, el sistema puede extraer una serie de funciones.

Métodos Sincrónicos Vs Métodos Asincrónicos

Tan pronto como se crea la cola, hay dos métodos para colocar trabajos en ella:

  • El método síncrono presenta una ubicación de tareas síncronas relacionadas con la cola actual. El método de sincronización devuelve el control a la cola actual después de que se haya completado toda la tarea y, por lo tanto, bloquea la cola actual.

  • El método asincrónico es una ubicación de tareas asincrónicas relacionada con la cola actual. Async en lugar del método de sincronización , devuelve el control a la cola actual inmediatamente después del inicio de la tarea en una cola diferente sin esperar a que finalice. Entonces, el método asíncrono no bloquea la ejecución de la tarea en la cola actual.

Una cola diferente puede ocurrir como:

  • Una cola en serie en caso de un método de ejecución asíncrono o concurrente en caso de ejecución sincrónica.

  • Una cola concurrente .

La tarea de un desarrollador radica exclusivamente en la selección de la cola y la adición de tareas (cierres) en esa cola de forma sincrónica con la ayuda del método de sincronización , o asincrónicamente, mediante el método asincrónico . iOS lo toma desde allí.

Colas de despacho global

Además de las colas de usuario personalizadas, iOS ofrece algunas colas de despacho globales listas para usar de cinco tipos:

  • Cola principal Responsable de todas las manipulaciones de la interfaz de usuario:

let main = DispatchQueue.main

Si necesita ejecutar una función o un cierre que afecte a la interfaz de usuario, cualquier cosa, esta función o cierre debe colocarse en la Cola principal, ya que esta es la cola de envío global de primera prioridad.

  • Cuatro colas simultáneas en segundo plano con diferentes capacidades y prioridades de QoS:

Más alta prioridad:

let userInteractiveQueue = DispatchQueue.global(qos: .userInteractive)

let userInitiatedQueue = DispatchQueue.global(qos: .userInitiated)

let utilityQueue = DispatchQueue.global(qos: .utility)

Prioridad más baja:

let backgroundQueue = DispatchQueue.global(.background)

Prioridad predeterminada:

let defaultQueue = DispatchQueue.global()

Apple ha equipado cada una de estas colas con una QoS abstracta y debe configurarse específicamente para cada tarea. La siguiente es la lista de QoS disponibles con sus breves descripciones de funciones:

  • .userInteractive es la calidad para las tareas que interactúan con un usuario en el mismo momento y toman poco o nada de tiempo. La ejecución de la animación es instantánea pero no en la cola principal. Por ejemplo, un usuario desliza la pantalla mientras el sistema realiza cálculos de procesamiento de imágenes en esa cola. El resultado se retrasa ligeramente al deslizar ya que los cálculos requieren algo de tiempo. Esta cola es de muy alta prioridad, aunque amante que de la cola principal.
  • .userInitiated es para las tareas iniciadas por un usuario y requiere comentarios fuera del evento interactivo. La retroalimentación es necesaria para que un usuario continúe la interacción, que puede demorar unos segundos, tiene alta prioridad pero es inferior a la de la cola anterior.
  • .utility es para las tareas que requieren cierto tiempo para ejecutarse y no necesitan comentarios inmediatos, por ejemplo, carga de datos y limpieza de la base de datos. Algunos servicios de mantenimiento del sistema se ejecutan sin intención del usuario, lo que puede llevar varios minutos. La prioridad es menor que la de la cola anterior.
  • .background es para las tareas no relacionadas con la visualización e irrelevantes para el tiempo de ejecución. Por ejemplo, copias de seguridad o sincronización de servicios web. Por lo general, se inician en segundo plano cuando no se requiere mantenimiento. La tarea en segundo plano puede tomar hasta horas y tiene la prioridad más baja entre todas las colas de despacho globales.

También hay una cola de concurrencia global llamada. predeterminado que informa la ausencia de información sobre la QoS. Esta cola se crea mediante el operador DispatchQueue.global () .

Problemas de concurrencia

Tan pronto como se otorgan capacidades de concurrencia a las tareas, van a la batalla por los recursos disponibles. Hay tres problemas principales asociados con esto:

  • Condición de carrera. Significa un error de proyección de sistema o aplicación multiproceso que ocurre cuando la operación del sistema o aplicación depende de cómo se ejecutan las artes del código.
  • Inversión prioritaria. Este problema se produce cuando existe una discrepancia lógica con las reglas de planificación, ya que la tarea de mayor prioridad está pendiente mientras la tarea de menor prioridad se ejecuta en función de la sucesión de la cola.
  • Punto muerto. Esta es la situación en un sistema multiproceso cuando varios subprocesos están en el estado de espera infinita de recursos, ocupados por estos mismos subprocesos.

En el desarrollo de iOS, la concurrencia se utiliza para mejorar el rendimiento de la aplicación al dividir las tareas en diferentes subprocesos capaces de operar de forma independiente. Sin embargo, la concurrencia es una condición difícil que requiere un análisis profundo de lo que se debe optimizar en términos de tareas y prioridades de la cola.

Shakuro | Diseño web y desarrollo

Como en la mayoría de las plataformas: ¡hay una biblioteca para eso!

En iOS específicamente, generalmente querrá leer sobre Grand Central Dispatch (GCD):

Grand Central Dispatch

Documentación de Apple

… o NSOperationQueue. Operation Queue en realidad usa GCD debajo del capó, por lo que podría considerarse una simple abstracción de GCD pero con mecanismos adicionales de cola y sincronización.

En NSHipster

Documentación de Apple

Hay algunas formas de manejar la programación concurrente en iOS, pero las más comunes son:

  • NSOperation
  • Grand Central Dispatch

Es útil conocer ambos, y puede aprender más leyendo la Guía de programación de simultaneidad en la biblioteca de desarrolladores de iOS.