Sobes.tech
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.