Sobes.tech
Middle+

Cum se poate implementa sincronizarea în Kotlin folosind coroutines?

sobes.tech AI

Răspuns de la AI

Sincronizarea în corutinele Kotlin se realizează cu ajutorul unor primitive și structuri de date speciale din biblioteca kotlinx.coroutines.

Abordări principale:

  1. Mutex

    • Oferă excludere mutuală pentru protejarea secțiunilor critice de cod.
    • O corutină care încearcă să obțină un Mutex se blochează până când acesta este eliberat.
    • Are funcții lock() și unlock(), precum și withLock mai idiomatic.
    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) // Așteptăm 10000
    }
    
  2. Semaphore

    • Limitează numărul de corutine care pot accesa simultan o resursă sau pot executa un anumit bloc de cod.
    • Gestionează un pool de permise.
    • Are funcții acquire() și release(), precum și withPermit.
    import kotlinx.coroutines.sync.Semaphore
    import kotlinx.coroutines.sync.withPermit
    import kotlinx.coroutines.*
    
    val semaphore = Semaphore(2) // Pot lucra simultan 2 corutine
    
    suspend fun doLimitedWork(id: Int) {
        semaphore.withPermit {
            println("Corutina $id a obținut un permis. Lucrează...")
            delay(100) // Imitație de muncă
            println("Corutina $id a eliberat permisul.")
        }
    }
    
    suspend fun main() = coroutineScope {
        repeat(5) { i ->
            launch {
                doLimitedWork(i)
            }
        }
    }
    
  3. Operații atomice (din kotlinx.coroutines.atomic)

    • Oferă operații thread-safe asupra tipurilor primitive și referințelor.
    • Utilizează instrucțiuni CPU de nivel jos (CAS - Compare-and-Swap).
    • Potrivit pentru operații simple fără blocări explicite.
    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) // Așteptăm 10000
    }
    
  4. Structuri de date partajate pe dispatcher single-thread

    • Abordare cea mai simplă. Rularea corutinelor pe un context monocord (Dispatchers.Default.limitedParallelism(1) sau newSingleThreadContext) garantează executarea secvențială a codului, evitând condițiile de cursă a datelor. Nu este o primitive de sincronizare explicită, dar asigură sincronizarea prin serializarea accesului.
    import kotlinx.coroutines.*
    
    val mySingleThreadContext = newSingleThreadContext("SingleThread")
    var sharedData = mutableListOf<Int>()
    
    suspend fun addToSharedData(value: Int) {
        sharedData.add(value) // În siguranță, deoarece rulează pe același fir
    }
    
    suspend fun main() = withContext(mySingleThreadContext) {
        repeat(10_000) {
            launch {
                 addToSharedData(it)
            }
        }
        println(sharedData.size) // Așteptăm 10000
    }
    
  5. Canale

    • Nu sunt primitive de sincronizare în forma lor pură, dar pot fi folosite pentru transmiterea sigură a datelor între corutine, ceea ce rezolvă indirect problemele de sincronizare a accesului la datele transmise.
    • Oferă un mod de transmitere a fluxului de date dintr-o corutină în alta.

Alegerea abordării depinde de scenariul de utilizare. Mutex și Semaphore oferă mecanisme clasice de blocare, operațiile atomice sunt eficiente pentru actualizări simple atomice, iar dispatcher-ul monocord este convenabil când accesul la datele comune trebuie să fie strict secvențial. Canalele sunt folosite pentru comunicare și coordonare, nu pentru protecția directă a resurselor comune.