Middle+
Proč se nedoporučuje používat bloky synchronizace a podobné konstrukce při práci s korutinami?
sobes.tech AI
Odpověď od AI
Použití blokujících konstrukcí, jako jsou synchronized nebo lock z knihovny standardních funkcí, při práci s korutinami vede k neefektivnímu využívání zdrojů a může způsobit problémy.
Hlavní důvody:
- Zámek vlákna: Synchronizační bloky zastavují provádění vlákna, nikoli pouze korutin. Pokud je uvnitř synchronizovaného bloku spuštěna Suspending-funkce, bude vlákno zablokováno, dokud Suspending-funkce neskončí a zámek nebude uvolněn. To může vést k "hladovění" jiných korutin využívajících stejné vlákno.
- Neslučitelnost se Suspending-funkcemi: Mechanismus práce s korutinami (přepínání kontextu, pokračování) nezohledňuje zámky na úrovni vláken. Spuštění Suspending-funkce uvnitř synchronizovaného bloku narušuje bezblokující povahu korutin.
- Porušení principů strukturované konkurence: Korutiny mají svůj systém označení zrušení a hierarchii. Zámky na úrovni vláken tento systém ignorují, což ztěžuje správné zpracování zrušení a řízení životního cyklu.
- Riziko deadlocků: Kombinace zámků vláken a mechanismů konkurence korutin zvyšuje pravděpodobnost vzniku složitých deadlocků, které jsou těžko debugovatelné.
Namísto zámků na úrovni vláken pro synchronizaci přístupu ke sdíleným zdrojům v korutinách by měly být používány specializované primitivy konkurence z knihovny kotlinx.coroutines:
Mutex: Zajišťuje vzájemné vyloučení. Korutina, která se pokusí získat obsazený mutex, pozastaví se (bez blokování vlákna) do doby, než bude mutex uvolněn.Semaphore: Omezuje počet korutin, které mohou současně přistupovat ke zdroji.Channel: Používá se pro výměnu dat mezi korutinami.Atomic variables: Pro jednoduché atomické operace.
Příklad použití 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á sekce
counter++
}
}
fun main() = runBlocking {
// Vytváříme množinu korutin pro konkurenční přístup
repeat(1000) {
launch {
incrementCounter()
}
}
delay(1000) // Dáváme čas korutinám na dokončení
println("Counter: $counter") // Očekáváme 1000
}
mutex.withLock je suspending-funkce. Pokud je mutex obsazen, korutina, která volá withLock, se pozastaví a uvolní vlákno pro jiné úkoly. Když je mutex dostupný, korutina se obnoví. To neblokuje vlákno, na rozdíl od synchronized.