Middle+
Como pode implementar a sincronização em Kotlin usando coroutines?
sobes.tech IA
Resposta da IA
A sincronização em corrotinas Kotlin é alcançada usando primitivas especiais e estruturas de dados da biblioteca kotlinx.coroutines.
Abordagens principais:
-
Mutex
- Fornece exclusão mútua para proteger seções críticas do código.
- A corrotina que tenta obter um Mutex fica bloqueada até que ele seja liberado.
- Possui funções
lock()eunlock(), bem comowithLockmais idiomático.
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.* val mutex = Mutex() var counter = 0 suspend fun increment() { mutex.withLock { counter++ } } suspend fun main() = coroutineScope { repeat(10_000) { launch { increment() } } println(counter) // Espera-se 10000 } -
Semaphore
- Limita o número de corrotinas que podem acessar um recurso ou executar um bloco de código simultaneamente.
- Gerencia um pool de permissões.
- Possui funções
acquire()erelease(), bem comowithPermit.
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.* val semaphore = Semaphore(2) // Até 2 corrotinas ao mesmo tempo suspend fun doLimitedWork(id: Int) { semaphore.withPermit { println("Corrotina $id adquiriu uma permissão. Trabalhando...") delay(100) // Simulação de trabalho println("Corrotina $id liberou uma permissão.") } } suspend fun main() = coroutineScope { repeat(5) { i -> launch { doLimitedWork(i) } } } -
**Operações atômicas (de
kotlinx.coroutines.atomic)- Oferecem operações seguras para threads sobre tipos primitivos e referências.
- Utilizam instruções de CPU de baixo nível (CAS - Compare-and-Swap).
- Adequadas para operações simples sem bloqueios explícitos.
import kotlinx.coroutines.atomic.AtomicInt import kotlinx.coroutines.* val atomicCounter = AtomicInt(0) suspend fun atomicIncrement() { atomicCounter.incrementAndGet() } suspend fun main() = coroutineScope { repeat(10_000) { launch { atomicIncrement() } } println(atomicCounter.value) // Espera-se 10000 } -
Estruturas de dados partilhadas num despachador de thread único
- Abordagem mais simples. Executar corrotinas num contexto de thread única (
Dispatchers.Default.limitedParallelism(1)ounewSingleThreadContext) garante execução sequencial do código, evitando condições de corrida. Não é uma primitiva de sincronização explícita, mas garante sincronização ao serializar o acesso.
import kotlinx.coroutines.* val mySingleThreadContext = newSingleThreadContext("SingleThread") var sharedData = mutableListOf<Int>() suspend fun addToSharedData(value: Int) { sharedData.add(value) // Seguro, pois executado numa única thread } suspend fun main() = withContext(mySingleThreadContext) { repeat(10_000) { launch { addToSharedData(it) } } println(sharedData.size) // Espera-se 10000 } - Abordagem mais simples. Executar corrotinas num contexto de thread única (
-
Canais
- Não são primitivas de sincronização em si, mas podem ser usados para transferir dados de forma segura entre corrotinas, resolvendo indiretamente problemas de sincronização de acesso aos dados transmitidos.
- Fornecem uma forma de transferir fluxo de dados de uma corrotina para outra.
A escolha da abordagem depende do cenário de uso. Mutex e Semaphore fornecem mecanismos clássicos de bloqueio, operações atômicas são eficientes para atualizações simples, e o despachador de thread única é conveniente quando o acesso a dados compartilhados deve ser estritamente sequencial. Canais são usados para comunicação e coordenação, não para proteção direta de recursos compartilhados.