Middle+
Prečo sa neodporúča používať bloky synchronizácie a podobné konštrukcie pri práci s korutinami?
sobes.tech AI
Odpoveď od AI
Použitie blokujúcich konštrukcií, ako sú synchronized alebo lock zo štandardnej knižnice, pri práci s korutinami vedie k neefektívnemu využívaniu zdrojov a môže spôsobiť problémy.
Hlavné dôvody:
- Zámok vlákna: Synchronizačné bloky zastavujú vykonávanie vlákna, nie len korutín. Ak je vo vnútri synchronizovaného bloku spustená Suspending-funkcia, vlákno bude zablokované, kým Suspending-funkcia neskončí a zámok nebude uvoľnený. To môže viesť k "hladovaniu" iných korutín využívajúcich to isté vlákno.
- Nesúlad so Suspending-funkciami: Mechanizmus práce s korutinami (prepínanie kontextu, pokračovania) nezohľadňuje zámky na úrovni vlákien. Spustenie Suspending-funkcie vo vnútri synchronizovaného bloku narušuje bezblokujúcu povahu korutín.
- Porušenie princípov štruktúrovanej konkurencie: Korutiny majú svoj systém označení zrušenia a hierarchiu. Zámky na úrovni vlákien tento systém ignorujú, čo sťažuje správne spracovanie zrušenia a riadenie životného cyklu.
- Riziko deadlockov: Kombinácia zámkov vlákien a mechanizmov konkurencie korutín zvyšuje pravdepodobnosť vzniku zložitých deadlockov, ktoré sú ťažko odstraňovateľné.
Namiesto zámkov na úrovni vlákien pre synchronizáciu prístupu ku zdieľaným zdrojom v korutinách by mali byť používané špecializované primitivy konkurencie z knižnice kotlinx.coroutines:
Mutex: Zabezpečuje vzájomné vylúčenie. Korutina, ktorá sa pokúsi získať obsadený mutex, pozastaví sa (bez blokovania vlákna) do doby, než bude mutex uvoľnený.Semaphore: Obmedzuje počet korutín, ktoré môžu súčasne pristupovať ku zdroju.Channel: Používa sa na výmenu dát medzi korutinami.Atomic variables: Pre jednoduché atomárne operácie.
Príklad použitia Mutex:
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.*
val mutex = Mutex()
var counter = 0
suspend fun incrementCounter() {
mutex.withLock {
// Kritická sekcia
counter++
}
}
fun main() = runBlocking {
// Vytvárame množinu korutín pre konkurenčný prístup
repeat(1000) {
launch {
incrementCounter()
}
}
delay(1000) // Dávame čas korutínam na dokončenie
println("Counter: $counter") // Očakávame 1000
}
mutex.withLock je suspending-funkcia. Ak je mutex obsadený, korutina, ktorá ho volá, sa pozastaví a uvoľní vlákno pre iné úlohy. Keď je mutex dostupný, korutina sa obnoví. Toto neblokuje vlákno, na rozdiel od synchronized.