Las funciones aisladas en un actor pueden ser re-ejecutadas incluso antes de que terminen de completarse, lo que puede traer problemas.
Al usar await dentro del método de un actor, esa tarea va a quedarse suspendida, mientras que otra tarea potencialmente puede empezar al mismo tiempo.
actor Counter {
private var value = 0
func count() {
Task {
value += 1
try? await Task.sleep(for: .seconds(1))
print("El contador va en \(value)")
}
}
}
let c = Counter()
await c.count()
await c.count()
await c.count()
try? await Task.sleep(for: .seconds(1.1))
En este caso:
-
count()se ejecuta por primera vez, se crea una nueva tarea, que incrementavaluea1, y se queda esperando. -
count()se ejecuta por segunda vez, se crea una nueva tarea, que incrementavaluea2, y se queda esperando. -
count()se ejecuta por tercera vez, se crea una nueva tarea que incrementavaluea3y se queda esperando. - La primera tarea termina de esperar e imprime el valor
value = 3. - La segunda tarea termina de esperar e imprime el valor
value = 3. - La tercera tarea termina de esperar e imprime el valor
value = 3.
Este proceso se denomina interleaving y ocurre en la ejecución de código asíncrono cuando hay un punto de suspensión.
Las tareas no se ejecutan en paralelo, sino que el actor solo puede ejecutar una tarea a la vez. El problema que estamos presenciando es un efecto secundario de una optimización de recursos, pues garantiza que el actor avanzará en algún trabajo que tenga pendiente. De hecho, el interleaving aparece como solución de los deadlocks, donde dos tareas son capaces de avanzar porque están se están esperando entre sí a que liberen algún recurso.
Top comments (0)