Middle+
Hogyan valósítható meg a szinkronizáció Kotlinben coroutine-ok használatával?
sobes.tech MI
Válasz az MI-től
A Kotlin coroutine-ok szinkronizálása speciális primitívek és adatszerkezetek segítségével történik a kotlinx.coroutines könyvtárból.
Fő megközelítések:
-
Mutex
- Kölcsönös kizárást biztosít a kritikus szekciók védelmére.
- Egy coroutine, amely megpróbálja megszerezni a Mutexet, blokkolódik, amíg az el nem szabadul.
lock()ésunlock()funkciókkal, valamint awithLockidiomatikusabb változatával rendelkezik.
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) // Várható eredmény 10000 } -
Semaphore
- Korlátozza az egyszerre hozzáférő coroutine-ok számát egy erőforráshoz vagy egy adott kódrészlethez.
- Engedélyek poolját kezeli.
acquire()ésrelease()funkciókkal, valamint awithPermit-tel rendelkezik.
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.* val semaphore = Semaphore(2) // Egyszerre 2 coroutine dolgozhat suspend fun doLimitedWork(id: Int) { semaphore.withPermit { println("Coroutine $id szerzett engedélyt. Dolgozik...") delay(100) // Munka imitálása println("Coroutine $id elengedte az engedélyt.") } } suspend fun main() = coroutineScope { repeat(5) { i -> launch { doLimitedWork(i) } } } -
Atomi műveletek (
kotlinx.coroutines.atomic-ból)- Szálbiztos műveleteket biztosít primitív típusokon és hivatkozásokon.
- Alacsony szintű CPU utasításokat (CAS - Compare-and-Swap) használ.
- Egyszerű műveletekhez, explicit zárak nélkül.
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) // Várható eredmény 10000 } -
Megosztott adatszerkezetek egypéldányos diszpécseren
- A legkönnyebb megközelítés. A coroutine-ok futtatása egy szálon belüli kontextusban (
Dispatchers.Default.limitedParallelism(1)vagynewSingleThreadContext) garantálja a kód szekvenciális végrehajtását, elkerülve az adatversenyeket. Nem nyilvánvaló primitív szinkronizáció, de a hozzáférés szekvencializálásával biztosítja a szinkronizációt.
import kotlinx.coroutines.* val mySingleThreadContext = newSingleThreadContext("SingleThread") var sharedData = mutableListOf<Int>() suspend fun addToSharedData(value: Int) { sharedData.add(value) // Biztonságos, mivel ugyanazon a szálon fut } suspend fun main() = withContext(mySingleThreadContext) { repeat(10_000) { launch { addToSharedData(it) } } println(sharedData.size) // Várható eredmény 10000 } - A legkönnyebb megközelítés. A coroutine-ok futtatása egy szálon belüli kontextusban (
-
Csatornák
- Nem primitív szinkronizációs eszközök, de biztonságos adatátvitelt tesznek lehetővé coroutine-ok között, közvetve megoldva az adatokhoz való hozzáférés szinkronizálását.
- Lehetővé teszik adatfolyamok átadását egyik coroutine-ból a másikba.
A megközelítés kiválasztása a felhasználási szcenáriótól függ. A Mutex és Semaphore klasszikus zárolási mechanizmusokat kínál, az atomikus műveletek hatékonyak egyszerű atomikus frissítésekhez, míg az egypéldányos diszpécser akkor hasznos, ha az adatokhoz való hozzáférésnek szigorúan szekvenciálisnak kell lennie. A csatornák kommunikációra és koordinációra szolgálnak, nem közvetlenül a közös erőforrások védelmére.