Async/Await (Parte 11 de 11)1

Paralelización con async let

2

Calcular tiempo de ejecución de un código

3

¿Qué es un Task?

4

¿Qué es un Actor y para que se utilizan?

5

¿Qué es Main Actor?

6

Modificador task

7

Cómo utilizar Task Group

8

Trabajando con iteraciones asíncronas usando AsyncSequence

9

Mezclando código síncrono y asíncrono usando CheckedContinuation

10

Optimiza la eficiencia de tu código concurrente con DiscardingTaskGroup

11

Explorando Task-local

Explorando Task-local

Con el nuevo modelo de concurrencia, el equipo detrás de Swift agrega más herramientas cada año para que podamos trabajar con código asíncrono de manera más estructurada. Este año, Apple introdujo Task-local, una nueva herramienta que nos ayuda a manejar almacenamiento que solo queremos que esté disponible dentro de un hilo.

Un Task-local es un dato asociado a una tarea específica, o más precisamente, a una jerarquía de tareas. Es como una variable global, pero el valor solo está disponible dentro de la jerarquía de tareas del contexto donde se ejecuta.

Ya existían APIs con comportamientos similares, como Task.currentPriority y Task.isCancelled, aunque Task-local no usa el mismo mecanismo de almacenaje. Esto nos muestra cómo este mismo patrón para acceder a metadatos en hilos está siendo usado en el nuevo paradigma de concurrencia moderna de Swift.

Características y limitantes de Task-local

Ahora veremos cómo podemos hacer uso de esta nueva herramienta y explicaremos sus ventajas y desventajas.

Crear una Task-local

Para crear un Task-local, debes anotar la variable usando el property wrapper @TaskLocal y que sea static. Ejemplo:

@TaskLocal static var myVariable: String = "No data"

Las variables que usen @TaskLocal pueden leerse en cualquier función dentro del contexto donde fue creada, incluyendo procesos síncronos.

Cada declaración de un Task-local representa su propio e independiente espacio de almacenamiento.

Leer un Task-local

Para acceder a los valores, lo hacemos como cualquier otra propiedad estática. Ejemplo:

func execute() async {
print(MyClass.myVariable)
}

Asignar un valor al Task-local

Podemos asignar un valor usando TaskLocal.withValue(_:operation:file:line:), pero no podremos cambiarlo luego. Veamos un ejemplo:

await MyClass.$myVariable.withValue("Hello") {
await execute()
}
await execute()

Es importante tener en cuenta que no puedes cambiar el valor de un Task-local luego de definido. En este código se imprimirá lo siguiente:

Como es código asíncrono podría variar el orden en que se imprimen los resultados.

Hello
No Data

Hello es el valor que estamos asignando a MyClass.myVariable en el contexto de la primera llamada a execute(), sin embargo, como en la segunda llamada no estamos asignando ningún valor, se imprime No Data, que es el valor por defecto.

El Task-local será retenido en memoria mientras exista el contexto donde fue ejecutado y todas las tareas hijas hayan terminado.

Cuando usar Task-local

Antes de determinar cuándo usarlo, es importante aclarar cuándo no. El objetivo de esta herramienta no es que la usemos para compartir estados globales de nuestras aplicaciones.

Aunque Task-local puede parecer útil, la recomendación es usarla solo si nuestro caso de uso no se resuelve simplemente pasando un parámetro. Acceder a un Task-local es más costoso que acceder a parámetros pasados explícitamente, además de que no tenemos ayuda en tiempo de compilación. Si olvidamos llamar a TaskLocal.withValue(_:operation:file:line:), podríamos introducir errores.

Task-local puede ser muy útil para instrumentación, como por ejemplo para crear un sistema para administrar logs.

Otras cosas a tomar en cuenta

  • Los tipos de datos alojados en un Task-local deben cumplir con Sendable. Pueden ser de tipo valor o de tipo referencia.
  • Cuando usamos tareas estructuradas, como TaskGroup, los Task-local son heredados por todas las tareas hijas. Sin embargo, las Task.detached no heredan. En el caso de Task { ... }, hereda los Task-local, ya que se hace una copia de los que se encontraban en el contexto al momento de su creación. Para más información sobre Task y Task.detached, puedes leer este artículo ¿Qué es un Task?.
  • El tiempo de vida de un Task-local está asociado al tiempo de vida de la tarea.
  • Cuando leemos el valor de un Task-local, se recorre el contexto donde se encuentra la tarea hasta que se encuentre una tarea padre donde se haya definido. Si no se encuentra ninguna, se retorna el valor por defecto, que puede ser nil si así se indica en la definición, por ejemplo:
@TaskLocal static var myVariable: String?

Conclusión

A manera de resumen veamos el siguiente código donde utilizamos Task-local de diferentes maneras:

execute() // Imprime: No data
await MyClass.$myVariable.withValue("Hello") {
print(MyClass.myVariable) // Imprime `Hello`
await MyLibrary.$requestID.withValue("Bye") {
await execute() // Imprime `Bye`
}
await execute() // prints: `Hello`
}
print(MyClass.myVariable) // Imprime: No data

Como podemos ver nuestro programa imprimirá Hello o Bye dependiendo el contexto y sin importar si la llamada es síncrona o asíncrona. En los casos donde no se ha indicaco un valor para myVariable, como el caso de la primera llamada a execute() y el último print(MyClass.myVariable) imprimirá No Data.

Task-local es una herramienta útil para compartir metadata en jerarquía de tareas, siempre y cuando estemos al tanto de sus limitantes.

Async/Await (Parte 11 de 11)1

Paralelización con async let

2

Calcular tiempo de ejecución de un código

3

¿Qué es un Task?

4

¿Qué es un Actor y para que se utilizan?

5

¿Qué es Main Actor?

6

Modificador task

7

Cómo utilizar Task Group

8

Trabajando con iteraciones asíncronas usando AsyncSequence

9

Mezclando código síncrono y asíncrono usando CheckedContinuation

10

Optimiza la eficiencia de tu código concurrente con DiscardingTaskGroup

11

Explorando Task-local

Comparte este artículo

Subscríbete a nuestro Newsletter

Mantente al día en el mundo de las aplicaciones móviles con nuestro blog especializado.

Artículos semanales

Todas las semanas artículos nuevos sobre el mundo de las aplicaciones móviles.

No spam

No te enviaremos spam, solo contenido de calidad. Puedes darte de baja cuando quieras.

Contenido de calidad

Nada de contenido generado de manera automática usando ChatGPT.

Recomendaciones

Tips indispensables sobre mejores prácticas y metodologías.

© 2024 AsyncLearn