DEV Community

GoyesDev
GoyesDev

Posted on

[SC] Prioridad de una tarea

Prioridad por defecto

Generalmente, un Task hereda la prioridad del contexto donde se creó. Esto es: otro Task, actor o TaskGroup. Si un Task "A" se crea desde una vista de SwiftUI, heredará la prioridad .userInitiated. Si un Task "B" se crea desde otro Task "C" que tenía una prioridad .low, entonces va a heredar la mis prioridad.

Sin embargo, al crear una tarea de tipo Task.detached, como no hereda la estructura de su contexto, entonces puede asumir una prioridad independiente, que por defecto es .medium.

Configurando la prioridad

  • userInitiated / high: Acciones que requieren feedback inmediato para el usuario. Generalmente el usuario las dispara.
  • utility / medium: Tareas más demoradas que no requieren resultados inmediatos.
  • background / low: Tareas de baja prioridad.

Heredando prioridad

Las Task.detached que no heredan la estructura del contexto donde se crearon NO heredan la prioridad del contexto.

Las Tasks de concurrencia estructurada (i.e. async let, await o TaskGroups) herendan el nivel de prioridad del contexto donde se crearon.

Por ejemplo:

Task(priority: .high) {
  await innerTask()

  Task.detached {
    print("Detached: \(Task.currentPriority)")
    // Imprime: Detached: TaskPriority.medium
  }
}

func innerTask() async {
  let priority: TaskPriority = Task.currentPriority
  print("Inner: \(Task.currentPriority)")
  // Imprime:  Inner: TaskPriority.high
}
Enter fullscreen mode Exit fullscreen mode

Scheduling

Generalmente se programa primero la tarea de más alta prioridad. Sin embargo, la decisión final depende del Executor.

Executors

Un Executor de Swift Concurrency es el encargado de decir cuándo ejecutar una tarea con base en factores como prioridad y recursos disponibles. El objetivo es evitar condiciones de carrera, optimizar desempeño y evitar inversiones de prioridad.

Por defecto el "Global Concurrency Pool" es un Executor que se asigna a los async task cuando se crean. Por otro lado, cada actor tiene su propio Executor para garantizar acceso seguro (en términos de concurrencia) a su estado.

Inversión de prioridad

La inversión de prioridad ocurre cuando una tarea de alta prioridad se queda atascada esperando a otra de menor prioridad. Ante este problema, hay dos casos donde la prioridad de una tarea se puede elevar temporalmente:

  1. Si una tarea de alta prioridad es encolada en un actor que está ejecutando una tarea de más baja prioridad, entonces eleva la prioridad de la tarea en curso para terminarla, y luego sigue con la tarea de alta prioridad encolada.
  2. Si una tarea de alta prioridad llama .value sobre el resultado de otra tarea de más baja prioridad, entonces la segunda tarea temporalmente se ejecutará con la prioridad más alta.

Comprobación sin mirar el texto

  1. ¿Qué prioridad tiene por defecto un Task.detached {}?
    .medium

  2. ¿Qué diferencia hay entre .high y .userInitiated?
    Tienen el mismo rawValue

  3. ¿Un async let hereda la prioridad del task padre?
    Sí.
    ¿Y un Task.detached?
    No.

  4. ¿En qué dos situaciones el sistema eleva la prioridad automáticamente?
    Cuando un actor está ejecutando una tarea de baja prioridad y le llega una de alta prioridad - Eleva temporalmente la prioridad de la tarea en curso.
    Cuando una tarea de alta prioridad invoca a otra de baja prioridad con .value.

  5. ¿Qué es un executor y cuál se usa por defecto?
    Se usa "Global Pool Executor" por defecto.
    Executor es un componente encargado de programar y ejecutar tareas con base en prioridades y recursos disponibles.

  6. ¿Qué problema resuelve la escalación de prioridad automática?
    Resuelve que una tarea de alta prioridad se quede atascada por otra de baja prioridad.


Bibliografía

Top comments (0)