DEV Community

GoyesDev
GoyesDev

Posted on

[GCD] Trabajando con DispatchWorkItem

Hasta ahora, el trabajo despachado a una DispatchQueue ha sido siempre un closure suelto: queue.async { ... }. Un DispatchWorkItem envuelve ese mismo bloque de código en un objeto, en lugar de pasarlo directamente. La diferencia práctica es que un closure suelto se pierde en el momento en que se despacha (no hay forma de referenciarlo después), mientras que un DispatchWorkItem se puede guardar en una variable y usar para esperar su finalización, recibir una notificación cuando termine, o cancelarlo con cancel(), tanto antes de que empiece a correr como mientras ya está corriendo (esto último se cubre en el artículo siguiente).

Creando y despachando un DispatchWorkItem

Consideremos el siguiente fragmento de código, donde se crea un DispatchWorkItem y se despacha con async, igual que se haría con un closure suelto:

let queue = DispatchQueue(label: "dev.goyes.gcd.workitem")
let workItem = DispatchWorkItem {
  print("Trabajo ejecutándose")
}

queue.async(execute: workItem)
Enter fullscreen mode Exit fullscreen mode

queue.async(execute:) y queue.sync(execute:) aceptan un DispatchWorkItem igual que aceptan un closure. Las reglas de sync vs. async y de cola serial vs. concurrente cubiertas en los artículos anteriores aplican exactamente igual: lo único que cambia es que ahora hay un objeto al que se puede volver después de despacharlo.

Ejecutarlo directamente con perform()

Un DispatchWorkItem no necesita una cola para correr. perform() ejecuta el bloque de forma síncrona, en el hilo que hace el llamado, sin pasar por ninguna cola.

Consideremos el siguiente fragmento de código, donde se llama perform() directamente:

let workItem = DispatchWorkItem {
  print("Corriendo en el hilo: \(Thread.current)")
}

workItem.perform()
// Corre de inmediato, en el mismo hilo que llama a perform(). Equivale a invocar el closure directamente.
Enter fullscreen mode Exit fullscreen mode

Esperar su finalización con wait()

wait() bloquea el hilo que hace el llamado hasta que el DispatchWorkItem termine de ejecutarse, sin importar en qué cola se haya despachado ni si se usó sync o async para despacharlo.

Consideremos el siguiente fragmento de código, donde se despacha un DispatchWorkItem con async y luego se espera su finalización por separado:

let queue = DispatchQueue(label: "dev.goyes.gcd.workitem")
let workItem = DispatchWorkItem {
  Thread.sleep(forTimeInterval: 1)
  print("Trabajo terminado")
}

queue.async(execute: workItem)
print("Despachado, sin esperar todavía")

workItem.wait()
print("Ahora sí terminó, un segundo después")
Enter fullscreen mode Exit fullscreen mode

wait() es útil precisamente porque se llama en un punto distinto al del despacho: el código puede seguir haciendo otras cosas entre queue.async(execute: workItem) y workItem.wait(), algo que no es posible con queue.sync, que bloquea de inmediato en el mismo punto donde se despacha.

Recibir una notificación al terminar con notify(queue:execute:)

En lugar de bloquear un hilo con wait(), notify(queue:execute:) registra un closure que se ejecuta automáticamente, en la cola indicada, cuando el DispatchWorkItem termina.

Consideremos el siguiente fragmento de código, donde se registra una notificación antes de despachar el trabajo:

let queue = DispatchQueue(label: "dev.goyes.gcd.workitem")
let workItem = DispatchWorkItem {
  Thread.sleep(forTimeInterval: 1)
  print("Trabajo terminado")
}

workItem.notify(queue: .main) {
  print("Notificación: el trabajo ya terminó, en el hilo principal")
}

queue.async(execute: workItem)
Enter fullscreen mode Exit fullscreen mode

notify no bloquea ningún hilo: el closure de notificación se encola en .main (o en la cola que se indique) automáticamente cuando el trabajo termina, sin que nada tenga que esperar activamente por él.

Lo que viene

Un DispatchWorkItem también se puede cancelar con cancel(), tanto antes de que empiece a correr (en ese caso nunca llega a ejecutarse) como mientras ya está corriendo (en ese caso queda marcado como cancelado en isCancelled, pero el bloque debe revisar esa propiedad y detenerse por sí mismo). Eso es lo que cubre el siguiente artículo.


Bibliografía

Top comments (0)