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