Middle+
De ce nu este recomandat să folosiți blocuri de sincronizare și construcții similare atunci când lucrați cu corutine?
sobes.tech AI
Răspuns de la AI
Utilizarea construcțiilor blocante, cum ar fi synchronized sau lock din biblioteca standard, atunci când lucrați cu corutine duce la o utilizare ineficientă a resurselor și poate cauza probleme.
Principalele motive:
- Blocarea thread-ului: Blocurile de sincronizare opresc executarea thread-ului, nu doar a corutinelor. Dacă o funcție Suspending este executată în interiorul unui bloc sincronizat, thread-ul va fi blocat până când funcția Suspending se va termina și blocarea va fi eliberată. Acest lucru poate duce la o "foame" pentru alte corutine care folosesc același thread.
- Incompatibilitate cu funcțiile Suspending: Mecanismul de lucru al corutinelor (schimbarea de context, continuările) nu ia în considerare blocările la nivel de thread. Executarea unei funcții Suspending în interiorul unui bloc sincronizat încalcă natura non-blocantă a corutinelor.
- Încălcarea principiilor de concurență structurată: Corutinele au propriul sistem de marcaje de anulare și ierarhie. Blocările la nivel de thread ignoră acest sistem, complicând gestionarea corectă a anulărilor și gestionarea ciclului de viață.
- Riscul de deadlock-uri: Combinația de blocări de thread și mecanisme de concurență ale corutinelor crește probabilitatea apariției deadlock-urilor complexe, dificil de depanat.
În loc de blocări la nivel de thread pentru sincronizarea accesului la resursele partajate în corutine, trebuie să folosiți primitive de concurență specializate din biblioteca kotlinx.coroutines:
Mutex: Asigură excludere mutuală. Corutina care încearcă să obțină un mutex ocupat se suspendă (fără a bloca thread-ul) până când mutex-ul este eliberat.Semaphore: Limitează numărul de corutine care au acces simultan la o resursă.Channel: Este utilizat pentru schimbul de date între corutine.Variabile atomice: Pentru operații atomice simple.
Exemplu de utilizare a 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 {
// Secțiunea critică
counter++
}
}
fun main() = runBlocking {
// Creăm mai multe corutine pentru acces concurent
repeat(1000) {
launch {
incrementCounter()
}
}
delay(1000) // Oferim timp corutinelor să ruleze
println("Counter: $counter") // Se așteaptă 1000
}
mutex.withLock este o funcție suspending. Dacă mutex-ul este ocupat, corutina care apelează withLock se suspendă și eliberează thread-ul pentru alte sarcini. Când mutex-ul devine disponibil, corutina continuă. Acest lucru nu blochează thread-ul, spre deosebire de synchronized.