Middle+
Por que não é recomendado usar blocos de sincronização e construções semelhantes ao trabalhar com corrotinas?
sobes.tech IA
Resposta da IA
A utilização de construções bloqueantes, como synchronized ou lock da biblioteca padrão, ao trabalhar com corutinas leva a um uso ineficiente dos recursos e pode causar problemas.
Razões principais:
- Bloqueio de thread: Os blocos de sincronização param a execução do thread, não apenas das corutinas. Se uma função Suspending for executada dentro de um bloco sincronizado, o thread ficará bloqueado até que a função Suspending termine e o bloqueio seja liberado. Isso pode levar a uma "fome" de outras corutinas que usam o mesmo thread.
- Incompatibilidade com funções Suspending: O mecanismo de trabalho das corutinas (troca de contexto, continuações) não considera os bloqueios ao nível de threads. Executar uma função Suspending dentro de um bloco sincronizado viola a natureza não bloqueante das corutinas.
- Violação dos princípios de concorrência estruturada: As corutinas têm seu próprio sistema de marcas de cancelamento e hierarquia. Os bloqueios ao nível de thread ignoram esse sistema, dificultando o manejo correto de cancelamentos e gerenciamento do ciclo de vida.
- Risco de deadlocks: A combinação de bloqueios de threads e mecanismos de concorrência de corutinas aumenta a probabilidade de deadlocks complexos, difíceis de depurar.
Em vez de bloqueios ao nível de thread para sincronizar o acesso a recursos compartilhados em corutinas, deve-se usar primitivas de concorrência especializadas da biblioteca kotlinx.coroutines:
Mutex: Garante exclusão mútua. A corutina que tenta adquirir um mutex ocupado se suspende (sem bloquear o thread) até que o mutex seja liberado.Semaphore: Limita a quantidade de corutinas que têm acesso simultâneo a um recurso.Channel: Usado para trocar dados entre corutinas.Variáveis atômicas: Para operações atômicas simples.
Exemplo 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 {
// Seção crítica
counter++
}
}
fun main() = runBlocking {
// Criar múltiplas corutinas para acesso concorrente
repeat(1000) {
launch {
incrementCounter()
}
}
delay(1000) // Dar tempo às corutinas para executarem
println("Counter: $counter") // Espera-se 1000
}
mutex.withLock é uma função suspending. Se o mutex estiver ocupado, a corutina que chamou withLock se suspende e libera o thread para outras tarefas. Quando o mutex fica disponível, a corutina é retomada. Isso não bloqueia o thread, ao contrário de synchronized.