Introducción a la concurrencia y el multihilo en Swift
Las aplicaciones móviles y de escritorio modernas deben ser rápidas y receptivas. En el desarrollo para iOS y macOS, la gestión adecuada de la concurrencia y el multihilo juega un papel clave. Swift proporciona potentes herramientas para la ejecución paralela de tareas, desde el clásico Grand Central Dispatch (GCD) hasta el moderno modelo async/await y los actores.
En este artículo, analizaremos todos los enfoques principales para trabajar con hilos en Swift: desde conceptos básicos hasta técnicas avanzadas. Aprenderá cómo evitar condiciones de carrera, interbloqueos y otros problemas típicos de la programación multihilo.
Grand Central Dispatch (GCD): el clásico del multihilo
Grand Central Dispatch (GCD) es una biblioteca de bajo nivel de Apple para gestionar la ejecución paralela de código. Utiliza colas (dispatch queues) para distribuir tareas entre los hilos.
Tipos principales de colas
En GCD existen dos tipos principales de colas:
- Colas seriales (secuenciales): las tareas se ejecutan una tras otra en el orden en que se añaden. Ideales para sincronizar el acceso a recursos.
- Colas concurrentes (paralelas): las tareas pueden ejecutarse simultáneamente; no se garantiza el orden de finalización.
Ejemplo de trabajo con GCD
import Foundation
// Creamos una cola seriallet serialQueue = DispatchQueue(label: "com.example.serial")
// Creamos una cola concurrentelet concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
// Ejecución asíncronaserialQueue.async { print("Tarea 1 ejecutándose en segundo plano")}
// Ejecución síncrona (bloquea el hilo actual)serialQueue.sync { print("Tarea 2 ejecutándose de forma síncrona")}
// Trabajo con el hilo principalDispatchQueue.main.async { print("Actualización de la UI en el hilo principal")}
// Ejecución con retardoDispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { print("Ejecutado después de 2 segundos")}Dispatch Group: sincronización de un grupo de tareas
Dispatch Group permite rastrear la finalización de un grupo de tareas asíncronas y ejecutar código después de que todas terminen.
let group = DispatchGroup()let queue = DispatchQueue.global(qos: .background)
for i in 1...5 { queue.async(group: group) { print("Tarea \\(i) iniciada") Thread.sleep(forTimeInterval: Double.random(in: 0.5...2.0)) print("Tarea \\(i) completada") }}
group.notify(queue: DispatchQueue.main) { print("¡Todas las tareas completadas! Actualizando la UI.")}Async/await moderno en Swift 5.5+
A partir de Swift 5.5, Apple presentó un nuevo modelo de concurrencia basado en las palabras clave async y await. Esto hace que el código asíncrono sea más legible y comprensible.
Definición de funciones asíncronas
Las funciones asíncronas se marcan con la palabra clave async, y su llamada requiere await:
import Foundation
// Función asíncrona para cargar datosfunc fetchUserData() async throws -> String { let url = URL(string: "https://api.example.com/user")! let (data, _) = try await URLSession.shared.data(from: url) return String(data: data, encoding: .utf8) ?? "Sin datos"}
// Uso de async/awaitfunc loadUserProfile() async { do { let userData = try await fetchUserData() await MainActor.run { print("Datos del usuario: \\(userData)") } } catch { print("Error de carga: \\(error)") }}
// Inicio de una tarea asíncronaTask { await loadUserProfile()}Async let: ejecución paralela
La palabra clave async let permite lanzar varias tareas asíncronas en paralelo:
<