Sobes.tech
Middle+

Kuidas saab Kotlinis korutinatega sünkroniseerimist rakendada?

sobes.tech AI

Vastus AI-lt

Kotlin korutinatesünteesi saavutatakse spetsiaalsete primitiivide ja andmestruktuuride abil kotlinx.coroutines teegist.

Peamised lähenemised:

  1. Mutex

    • Tagab vastastikuse välistuse kriitilistes sektsioonide kaitsmiseks.
    • Korutin, mis üritab Mutex-i saada, blokeerub kuni see vabastatakse.
    • Omab lock() ja unlock() funktsioone ning ka rohkem idiomaatilist 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) // Ootame 10000
    }
    
  2. Semaphore

    • Piirab samaaegselt töötavate korutinote arvu, mis saavad samaaegselt ressursile ligi või täita teatud koodi.
    • Halda loa pulti.
    • Omab acquire() ja release() funktsioone ning withPermit.
    import kotlinx.coroutines.sync.Semaphore
    import kotlinx.coroutines.sync.withPermit
    import kotlinx.coroutines.*
    
    val semaphore = Semaphore(2) // Võib töötada korraga 2 korutinat
    
    suspend fun doLimitedWork(id: Int) {
        semaphore.withPermit {
            println("Korutina $id omandas loa. Töö käib...")
            delay(100) // Töö imitatsioon
            println("Korutina $id vabastas loa.")
        }
    }
    
    suspend fun main() = coroutineScope {
        repeat(5) { i ->
            launch {
                doLimitedWork(i)
            }
        }
    }
    
  3. Atomaarsed operatsioonid (kotlinx.coroutines.atomic-ist)

    • Tagab voogudele ohutud operatsioonid primitiivide ja viidete üle.
    • Kasutab madala taseme CPU juhiseid (CAS - Compare-and-Swap).
    • Sobib lihtsateks operatsioonideks ilma ilmsete lukudeta.
    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) // Ootame 10000
    }
    
  4. Jagatud andmestruktuurid ühe-joonelise saatjaga

    • Kõige lihtsam lähenemine. Dispatchers.Default.limitedParallelism(1) või newSingleThreadContext võimaldab käivitada korutinaid ühe joone kontekstis, tagades järjekindla täitmise ja vältides andmerünnakuid. See ei ole otsene sünkroniseerimise primitiiv, kuid tagab sünkroniseerimise serialiseerimise kaudu.
    import kotlinx.coroutines.*
    
    val mySingleThreadContext = newSingleThreadContext("SingleThread")
    var sharedData = mutableListOf<Int>()
    
    suspend fun addToSharedData(value: Int) {
        sharedData.add(value) // Ohutu, kuna toimub ühes joones
    }
    
    suspend fun main() = withContext(mySingleThreadContext) {
        repeat(10_000) {
            launch {
                 addToSharedData(it)
            }
        }
        println(sharedData.size) // Ootame 10000
    }
    
  5. ** Kanalid**

    • Ei ole primitiivsed sünkroniseerimiseks, kuid võivad olla kasutatavad turvaliseks andmeedastuseks korutinide vahel, mis kaudselt lahendab juurdepääsu probleemid edastatavate andmete osas.
    • Pakuvad võimalust andmevoo edastamiseks ühest korutinas teise.

Valik sõltub kasutusstsenaariumist. Mutex ja Semaphore pakuvad klassikalisi lukustusmehhanisme, Atomaarsete operatsioonide tõhusus on lihtsate atomaarsete uuenduste jaoks, ning ühe-joonelise saatja kasutamine on mugav, kui juurdepääs ühistele andmetele peab olema rangelt järjestikune. Kanalid kasutatakse kommunikatsiooniks ja koordineerimiseks, mitte otse ühiste ressursside kaitseks.