Sobes.tech
Middle+

Kaip galima įgyvendinti sinchronizaciją Kotlin naudojant korutinas?

sobes.tech AI

Atsakymas iš AI

Kotlin korutinų sinchronizacija pasiekiama naudojant specialius primitivius ir duomenų struktūras iš kotlinx.coroutines bibliotekos.

Pagrindiniai požiūriai:

  1. Mutex

    • Užtikrina tarpusavio išimtį kritinėse sekcijose.
    • Korutina, bandanti gauti Mutex, blokuojama iki kol jis bus laisvas.
    • Turi lock() ir unlock() funkcijas, taip pat labiau idiomatinius 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) // Tikimės 10000
    }
    
  2. Semaphore

    • Ribojamas vienu metu veikiančių korutinų skaičius, kurie gali vienu metu pasiekti išteklių arba vykdyti tam tikrą bloką.
    • Valdo leidimų pulą.
    • Turi acquire() ir release() funkcijas, taip pat withPermit.
    import kotlinx.coroutines.sync.Semaphore
    import kotlinx.coroutines.sync.withPermit
    import kotlinx.coroutines.*
    
    val semaphore = Semaphore(2) // Vienu metu gali dirbti 2 korutinos
    
    suspend fun doLimitedWork(id: Int) {
        semaphore.withPermit {
            println("Korutina $id įgijo leidimą. Dirba...")
            delay(100) // Darbo imitacija
            println("Korutina $id paleidžia leidimą.")
        }
    }
    
    suspend fun main() = coroutineScope {
        repeat(5) { i ->
            launch {
                doLimitedWork(i)
            }
        }
    }
    
  3. Atomarinės operacijos (kotlinx.coroutines.atomic iš)

    • Užtikrina srauto saugias operacijas primitives ir nuorodose.
    • Naudoja žemo lygio CPU instrukcijas (CAS - Compare-and-Swap).
    • Tinka paprastoms operacijoms be aiškių užraktų.
    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) // Tikimės 10000
    }
    
  4. Bendrai naudojamos duomenų struktūros vieno srauto dispečeriu

    • Labiausiai paprastas požiūris. Dispatchers.Default.limitedParallelism(1) arba newSingleThreadContext leidžia vykdyti korutinas vieno srauto kontekste, garantuojant eilės vykdymą ir išvengiant duomenų varžybų. Tai nėra aiškus sinchronizacijos primitivas, bet užtikrina sinchronizaciją serializacijos būdu.
    import kotlinx.coroutines.*
    
    val mySingleThreadContext = newSingleThreadContext("SingleThread")
    var sharedData = mutableListOf<Int>()
    
    suspend fun addToSharedData(value: Int) {
        sharedData.add(value) // Saugu, nes vykdoma viename sraute
    }
    
    suspend fun main() = withContext(mySingleThreadContext) {
        repeat(10_000) {
            launch {
                 addToSharedData(it)
            }
        }
        println(sharedData.size) // Tikimės 10000
    }
    
  5. Kanalai

    • Nėra sinchronizacijos primitivas, bet gali būti naudojami saugiam duomenų perdavimui tarp korutinų, kas netiesiogiai sprendžia prieigos prie perduodamų duomenų problemas.
    • Suteikia būdą perduoti duomenų srautą iš vienos korutinos į kitą.

Pasirinkimas priklauso nuo naudojimo scenarijaus. Mutex ir Semaphore siūlo klasikinius blokavimo mechanizmus, Atomarinės operacijos yra efektyvios paprastoms atominėms atnaujinimams, o vieno srauto dispečeriai patogūs, kai prieiga prie bendrų duomenų turi būti griežtai serializuota. Kanalai naudojami komunikacijai ir koordinacijai, o ne tiesiogiai bendrų išteklių apsaugai.