Preguntas
¿Qué es exactamente un hilo y por qué tiene un alto costo de creación y cambio de contexto?
Un hilo es un recurso, manejado por el sistema operativo, que ejecuta una secuencia de instrucciones. Crear hilos y cambiar el contexto entre ellos es una operación muy costosa.
Cada hilo requiere su propia pila de memoria (stack) y el cambio de contexto obliga al CPU a guardar y restaurar el estado completo del hilo (registros, puntero de instrucción, etc).
¿Por qué Swift Concurrency no garantiza en qué hilo se ejecutará una función asíncrona?
Swift Concurrency dispone de un pool global concurrente de hilos (tantos como el número de procesadores del dispositivo).
Para optimizar el uso de hilos, se despacha una Task en cualquier hilo disponible. Cuando hay un punto de suspensión (await), el runtime de Swift Concurrency cede el hilo a cualquier otra tarea. Luego, cuando la Task recupera el contexto, el runtime de SC le asigna cualquier hilo disponible - que puede ser el que tenía inicialmente, pero no está garantizado, porque el sistema pudo haberlo asignado a otra Task.
Aquí quien limita los hilos no es el sistema operativo sino el framework Swift Concurrency. El sistema operativo podría crear muchos más hilos (como lo hacía GCD). Es una decisión de diseño del framework, no una restricción del sistema operativo.
¿Qué ocurre exactamente cuando una tarea llega a un punto de suspensión con await?
El sistema suspende la Task y devuelve el hilo al pool global de hilos - Lo que significa que está disponible para que cualquier otra Task lo tome.
¿Cómo funciona el cooperative thread pool y por qué limita los hilos al número de núcleos de CPU?
Para no tener "thread explosion" y así degradar el desempeño de la aplicación debido al cambio de contexto entre hilos y posibles problemas de inversión de prioridades, el sistema operativo limita la creación de hilos al número de núcleos del procesador.
El "cooperative thread pool" son todos los hilos disponibles para ser usados por las distintas Tasks.
¿Qué es el thread explosion y cómo lo evita Swift Concurrency?
"thread explosion" ocurre porque se crean demasiados hilos y aparecen bloqueos que provocan:
- Desperdicio de memoria debido a los hilos detenidos.
- Intercambio excesivo de contexto (que reduce la eficiencia del procesador)
- Problemas de inversión de prioridad.
¿Por qué tener menos hilos no reduce el rendimiento comparado con GCD?
Aunque es contraintuitivo, el intercambio innecesario de contexto y el desperdicio de memoria son problemas que se reducen al tener menos hilos.
¿Qué demuestra el ejemplo de código con ThreadingDemonstrator sobre la relación entre tareas e hilos?
private struct ThreadingDemonstrator {
private func firstTask() async throws {
print("Task 1 started on thread: \(Thread.currentThread)")
try await Task.sleep(for: .seconds(2))
print("Task 1 resumed on thread: \(Thread.currentThread)")
}
private func secondTask() async {
print("Task 2 started on thread: \(Thread.currentThread)")
}
func demonstrate() {
Task {
try await firstTask()
}
Task {
await secondTask()
}
}
}
struct ThreadingDemonstratorTests {
@Test
func execute() async throws {
let sut = ThreadingDemonstrator()
sut.demonstrate()
try await Task.sleep(for: .seconds(3))
}
}
- No se puede garantizar el orden de ejecución de las tareas
firstTaskysecondTask. Para hacerlo hay que usarawait. - Cuando se asigna un hilo a una tarea y esta se suspende, no se puede garantizar que lo recupere una vez se reanude.
¿Por qué la tarea 1 puede reanudarse en un hilo diferente al que inició?
Cuando se suspende la tarea 1, cede ("yield") el hilo que tenía y el sistema operativo es libre de asignárselo a cualquier otra tarea.
¿Qué diferencia introduce el modo Swift 6 respecto a acceder a Thread.current y cómo se resuelve?
Usar Thread.current desde un contexto asíncrono arrojará el error de compilación: Class property 'current' is unavailable from asynchronous contexts; Thread.current cannot be used from async contexts..
La solución fue envolver el llamado de Thread.current en un método (variable computada) nonisolated static:
extension Thread {
public nonisolated static var currentThread: Thread {
return Thread.current
}
}
Top comments (0)