Concurrencia y subprocesamiento múltiple en Swift: nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Concurrencia y subprocesamiento múltiple en Swift: nivel avanzado

\n\n

Las aplicaciones modernas para iOS y macOS requieren un alto rendimiento y capacidad de respuesta de la interfaz. El trabajo avanzado con concurrencia en Swift va mucho más allá del simple uso de Grand Central Dispatch (GCD). En este artículo, exploraremos las herramientas clave de Swift 5.5+ para gestionar tareas asíncronas: async/await, Actores, TaskGroup y el protocolo Sendable. Aprenderá cómo evitar condiciones de carrera, estructurar correctamente el código de subprocesamiento múltiple y optimizar el rendimiento.

\n\n

1. Fundamentos de async/await y Concurrencia Estructurada

\n\n

Las palabras clave async y await son la base de la concurrencia moderna en Swift. Permiten escribir código asíncrono que se ve como síncrono, lo que simplifica enormemente la lectura y la depuración.

\n\n

Declaración de una función asíncrona:

\n
func fetchUserData() async throws -> User {\n    let url = URL(string: "https://api.example.com/user")!\n    let (data, _) = try await URLSession.shared.data(from: url)\n    let decoder = JSONDecoder()\n    return try decoder.decode(User.self, from: data)\n}
\n\n

Llamada a una función asíncrona con manejo de errores:

\n
Task {\n    do {\n        let user = try await fetchUserData()\n        print("User name: \\(user.name)")\n    } catch {\n        print("Failed to fetch user: \\(error)")\n    }\n}
\n\n

Puntos importantes:

\n
    \n
  • Concurrencia Estructurada: Cada tarea asíncrona tiene un ciclo de vida claro, limitado por el ámbito de la Task padre.
  • \n
  • Cancelación de tareas: Cancelación automática de tareas hijas al cancelar la Task padre.
  • \n
  • Prioridades: Use Task(priority: .userInitiated) para tareas importantes para el usuario.
  • \n
\n\n

2. Actores: protección contra condiciones de carrera

\n\n

Antes de Swift 5.5, los desarrolladores solían usar colas serializadas de GCD para proteger el estado compartido. Los Actores proporcionan aislamiento incorporado: el acceso al estado del actor solo es posible a través de await, lo que garantiza la seguridad de subprocesos a nivel del compilador.

\n\n

Ejemplo de Actor para una cuenta bancaria:

\n
actor BankAccount {\n    private var balance: Double = 0.0\n\n    func deposit(amount: Double) {\n        balance += amount\n    }\n\n    func withdraw(amount: Double) throws {\n        guard balance >= amount else {\n            throw BankError.insufficientFunds\n        }\n        balance -= amount\n    }\n\n    func getBalance() -> Double {\n        return balance\n    }\n}\n\n// Uso\nlet account = BankAccount()\nTask {\n    await account.deposit(amount: 100.0)\n    let currentBalance = await account.getBalance()\n    print("Balance: \\(currentBalance)")\n}
\n\n

Ventajas de los Actores:

\n
    \n
  • Verificación del compilador: Intentar acceder a una propiedad del actor sin await generará un error de compilación.
  • \n
  • Reentrada (Reentrancy): Un actor puede pausar la ejecución del método actual (por ejemplo, al usar await dentro del actor) y procesar otra llamada, lo que previene bloqueos.
  • \n
  • Rendimiento: Los actores utilizan mecanismos de sincronización optimizados, a menudo más rápidos que los bloqueos manuales.
  • \n
\n\n

3. TaskGroup: procesamiento paralelo de datos

\n\n

Cuando necesita ejecutar varias operaciones asíncronas independientes y esperar a que todas terminen, TaskGroup es la herramienta ideal. Gestiona automáticamente la cantidad de tareas paralelas y recopila los resultados.

\n\n

Ejemplo de descarga paralela de varias imágenes:

\n
func downloadImages(urls: [URL])                

Blogs

Book Recommendations