Middle+
¿Por qué no se recomienda usar bloques de sincronización y construcciones similares al trabajar con corutinas?
sobes.tech AI
Respuesta de la IA
El uso de construcciones bloqueantes, como synchronized o lock de la biblioteca estándar, al trabajar con corutinas conduce a un uso ineficiente de los recursos y puede causar problemas.
Razones principales:
- Bloqueo de hilo: Los bloques de sincronización detienen la ejecución del hilo, no solo de las corutinas. Si dentro de un bloque sincronizado se ejecuta una función Suspending, el hilo quedará bloqueado hasta que la función Suspending termine y se libere el bloqueo. Esto puede llevar a un "hambre" de otras corutinas que usan el mismo hilo.
- Incompatibilidad con funciones Suspending: El mecanismo de trabajo de las corutinas (cambio de contexto, continuaciones) no considera los bloqueos a nivel de hilos. Ejecutar una función Suspending dentro de un bloque sincronizado viola la naturaleza no bloqueante de las corutinas.
- Violación de los principios de concurrencia estructurada: Las corutinas tienen su propio sistema de marcas de cancelación y jerarquía. Los bloqueos a nivel de hilo ignoran este sistema, dificultando el manejo correcto de cancelaciones y la gestión del ciclo de vida.
- Riesgo de deadlocks: La combinación de bloqueos de hilos y mecanismos de concurrencia de corutinas aumenta la probabilidad de deadlocks complejos, difíciles de depurar.
En lugar de bloqueos a nivel de hilo para sincronizar el acceso a recursos compartidos en corutinas, se deben usar primitivas de concurrencia especializadas de la biblioteca kotlinx.coroutines:
Mutex: Garantiza exclusión mutua. La corutina que intenta adquirir un mutex ocupado se suspende (sin bloquear el hilo) hasta que el mutex sea liberado.Semaphore: Limita la cantidad de corutinas que tienen acceso simultáneo a un recurso.Channel: Se usa para intercambiar datos entre corutinas.Variables atómicas: Para operaciones atómicas simples.
Ejemplo de uso de Mutex:
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.*
val mutex = Mutex()
var counter = 0
suspend fun incrementCounter() {
mutex.withLock {
// Sección crítica
counter++
}
}
fun main() = runBlocking {
// Crear múltiples corutinas para acceso concurrente
repeat(1000) {
launch {
incrementCounter()
}
}
delay(1000) // Dar tiempo a las corutinas para que se ejecuten
println("Counter: $counter") // Se espera 1000
}
mutex.withLock es una función suspending. Si el mutex está ocupado, la corutina que llama a withLock se suspende y libera el hilo para otras tareas. Cuando el mutex se vuelve disponible, la corutina se reanuda. Esto no bloquea el hilo, a diferencia de synchronized.