Preguntas
¿Cuál es la diferencia entre el comportamiento anterior y el nuevo de las funciones nonisolated async a partir de Swift 6.2?
Antes de Swift 6.2, las funciones nonisolated async se ejecutaban de forma independiente, sin heredar el contexto de ejecución. Esto significa que cada función nonisolated async es una sub-tarea que decide por sí misma dónde ejecutarse. Si se anota con @MainActor o algún @globalActor, entonces se va a ejecutar en el executor de dicho actor. En caso contrario, se va a ir a un hilo de segundo plano.
A partir de Swift 6.2 las funciones nonisolated async corren, por defecto, en al actor invocador.
Para rechazar la herencia de dominio de aislamiento, se debe marcar el método específicamente con el atributo @concurrent.
¿Bajo qué circunstancias una tarea (Task) puede ejecutarse en el hilo principal en lugar de en un hilo en segundo plano?
Una Task se puede ejecutar en el hilo principal si está marcada con @MainActor, o si fue invocada por una tarea marcada con @MainActor en Swift 6.2.
¿Para qué sirve el atributo @concurrent y cuándo se debe usar?
@concurrent sirve para que un método nonisolated async NO herede el contexto del método invocador.
¿Qué problema resuelve nonisolated(nonsending) y cuándo es apropiado aplicarlo?
En caso de que la propiedad NonisolatedNonsendingByDefault esté puesta en NO, puede aparecer un error como Sending value of non-Sendable type ‘MyType’ risks causing data races.
Este error aparece porque Swift asume que, sin nonisolated(nonsending), la función va a correr en un dominio de aislamiento diferente al del llamador. Cuando eso pasa, Swift necesita enviar el valor (selfu otros parámetros) de un dominio a otro - y si el tipo no esSendable`, el compilador dice: "Peligro, datarace!".
nonisolated(nonsending) le dice al compilador: "Esta función va a heredar el dominio de aislamiento del llamador, así que no hay ningún envío entre dominios".
Si no hay envío, no hay riesgo de data race.
nonisolated(nonsending) no es una solución mágica universal. Funciona solo cuando de verdad estoy de acuerdo con que la función bloquee el actor del llamador.
Si la función hace algo pesado, usar nonisolated(nonsending) podría bloquear el hilo en cuestión. En ese caso, la solución correcta sería hacer el tipo Sendable.
¿Cómo se puede verificar en qué hilo se está ejecutando una tarea durante el desarrollo?
Usar Thread.current. Teniendo en cuenta que posiblemente sea necesario usar la extensión:
swift
extension Thread {
public static var currentThread: Thread {
return Thread.current
}
}
Por otro lado, siempre se puede poner un punto de quiebre para mirar en qué hilo se está parado.
¿cómo explica el autor que el actor de llamada puede influir en el hilo donde se ejecuta una tarea?
El actor del llamador determina dónde empieza la tarea y a dónde regresa después de cada punto de suspensión.
En el ejemplo del artículo:
`swift
@MainActor
private func updateUI() {
Task {
print("Starting on the main thread")
await someBackgroundTask()
print("Resuming on the main thread")
}
}
private func someBackgroundTask() async {
print("Background task on thread: 8")
}
`
someBackgroundTask() corre en el hilo 8. No obstante, la primera línea del Task de updateUI() corre en el hilo 1, y después de la suspensión de await someBackgroundTask()`, el trabajo regresa al hilo 1.
¿Qué cambio introduce SE-461 y cuál es la justificación filosófica que da el autor al citarlo?
SE-461: Run nonisolated async functions on the caller’s actor by default hace que las funciones nonisolated async hereden el contexto del método invocador. En caso de no querer que esto pase, entonces hay que marcar con el atributo @concurrent.
El comportamiento anterior de las funciones nonisolated async sacrificaba usabilidad innecesariamente para proteger la responsividad del hilo principal.
Top comments (0)