Middle+
Jak implementovat synchronizaci v Kotlinu pomocí coroutin?
sobes.tech AI
Odpověď od AI
Synchronizace v Kotlin korutinách je dosažena pomocí speciálních primitiv a datových struktur z knihovny kotlinx.coroutines.
Hlavní přístupy:
-
Mutex
- Poskytuje vzájemné vyloučení pro ochranu kritických sekcí kódu.
- Korutina, která se pokusí získat Mutex, je zablokována, dokud není uvolněn.
- Má funkce
lock()aunlock(), stejně jako idiomatický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) // Očekáváme 10000 } -
Semaphore
- Omezí počet korutin, které mohou současně získat přístup k zdroji nebo vykonat určitý blok kódu.
- Řídí skupinu povolení.
- Má funkce
acquire()arelease(), stejně jakowithPermit.
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.* val semaphore = Semaphore(2) // Současně může pracovat 2 korutiny suspend fun doLimitedWork(id: Int) { semaphore.withPermit { println("Korutina $id získala povolení. Pracuji...") delay(100) // Simulace práce println("Korutina $id uvolnila povolení.") } } suspend fun main() = coroutineScope { repeat(5) { i -> launch { doLimitedWork(i) } } } -
Atomické operace (z
kotlinx.coroutines.atomic)- Poskytují bezpečné operace nad primitivními typy a odkazy.
- Používají nízkoúrovňové CPU instrukce (CAS - Compare-and-Swap).
- Vhodné pro jednoduché operace bez explicitního zámku.
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) // Očekáváme 10000 } -
Sdílené datové struktury na dispatcheru s jedním vláknem
- Nejjednodušší přístup. Spuštění korutin na jednovláknovém kontextu (
Dispatchers.Default.limitedParallelism(1)nebonewSingleThreadContext) zaručuje sekvenční provádění kódu, čímž se vyloučí závody dat. Není to explicitní primitiv synchronizace, ale zajišťuje synchronizaci serializací přístupu.
import kotlinx.coroutines.* val mySingleThreadContext = newSingleThreadContext("SingleThread") var sharedData = mutableListOf<Int>() suspend fun addToSharedData(value: Int) { sharedData.add(value) // Bezpečné, protože běží na jednom vlákně } suspend fun main() = withContext(mySingleThreadContext) { repeat(10_000) { launch { addToSharedData(it) } } println(sharedData.size) // Očekáváme 10000 } - Nejjednodušší přístup. Spuštění korutin na jednovláknovém kontextu (
-
Kanály
- Nejsou primitivy synchronizace v čisté podobě, ale mohou být použity pro bezpečný přenos dat mezi korutinami, čímž nepřímo řeší problémy synchronizace přístupu k předávaným datům.
- Poskytují způsob přenosu toku dat z jedné korutiny do druhé.
Volba přístupu závisí na scénáři použití. Mutex a Semaphore poskytují klasické mechanismy blokování, atomické operace jsou efektivní pro jednoduché atomické aktualizace, a dispatcher s jedním vláknem je vhodný, když má být přístup k sdíleným datům striktně sekvenční. Kanály se používají pro komunikaci a koordinaci, nikoli přímo pro ochranu sdílených zdrojů.