Sobes.tech
Middle+

Kā var īstenot sinhronizāciju Kotlin, izmantojot korutinas?

sobes.tech AI

Atbilde no AI

Kotlin korutīnu sinhronizācija tiek panākta ar speciāliem primitīviem un datu struktūrām no kotlinx.coroutines bibliotēkas.

Galvenie pieejas veidi:

  1. Mutex

    • Nodrošina savstarpēju izslēgšanu kritiskajās sadaļās.
    • Korutīna, kas mēģina iegūt Mutex, tiek bloķēta līdz tas kļūst brīvs.
    • Tam ir lock() un unlock() funkcijas, kā arī vairāk idiomātiska withLock.
    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) // Sagaidām 10000
    }
    
  2. Semaphore

    • Ierobežo vienlaicīgi darbojošo korutīnu skaitu, kas var piekļūt resursam vai veikt noteiktu kodu.
    • Pārvalda atļauju pulku.
    • Tam ir acquire() un release() funkcijas, kā arī withPermit.
    import kotlinx.coroutines.sync.Semaphore
    import kotlinx.coroutines.sync.withPermit
    import kotlinx.coroutines.*
    
    val semaphore = Semaphore(2) // Vienlaicīgi var strādāt 2 korutīnas
    
    suspend fun doLimitedWork(id: Int) {
        semaphore.withPermit {
            println("Korutīna $id ieguvusi atļauju. Strādā...")
            delay(100) // Darba imitācija
            println("Korutīna $id atbrīvo atļauju.")
        }
    }
    
    suspend fun main() = coroutineScope {
        repeat(5) { i ->
            launch {
                doLimitedWork(i)
            }
        }
    }
    
  3. Atomāras operācijas (kotlinx.coroutines.atomic-a)

    • Nodrošina drošas darbības primitives un saitēm.
    • Izmanto zemākā līmeņa CPU instrukcijas (CAS - Compare-and-Swap).
    • Piemērots vienkāršām operācijām bez skaidriem bloķētājiem.
    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) // Sagaidām 10000
    }
    
  4. Koplietojamās datu struktūras vienas straumes dispečerī

    • Visvienkāršākais pieejas veids. Dispatchers.Default.limitedParallelism(1) vai newSingleThreadContext ļauj palaist korutinas vienas straumes kontekstā, nodrošinot secīgu izpildi un novēršot datu sacensības. Tas nav skaidrs sinhronizācijas primitives, bet nodrošina sinhronizāciju ar serializāciju.
    import kotlinx.coroutines.*
    
    val mySingleThreadContext = newSingleThreadContext("SingleThread")
    var sharedData = mutableListOf<Int>()
    
    suspend fun addToSharedData(value: Int) {
        sharedData.add(value) // Droši, jo tiek veikts vienā straumē
    }
    
    suspend fun main() = withContext(mySingleThreadContext) {
        repeat(10_000) {
            launch {
                 addToSharedData(it)
            }
        }
        println(sharedData.size) // Sagaidām 10000
    }
    
  5. Kanāli

    • Nav primitives sinhronizācijai, bet var tikt izmantoti drošai datu pārraidīšanai starp korutīnām, kas netieši risina piekļuves problēmas pie pārraidītajiem datiem.
    • Nodrošina datu plūsmas pārraidīšanas veidu no vienas korutinas uz otru.

Izvēle ir atkarīga no lietošanas scenārija. Mutex un Semaphore nodrošina klasiskus bloķēšanas mehānismus, Atomāras operācijas ir efektīvas vienkāršām atomārām atjauninājumiem, bet vienas straumes dispečeri ir ērti, kad piekļuve kopīgajiem datiem ir stingri secīga. Kanāli tiek izmantoti komunikācijai un koordinācijai, nevis tiešai kopīgu resursu aizsardzībai.