Sobes.tech
Middle+

Miért nem ajánlott szinkronizációs blokkokat és hasonló szerkezeteket használni korutinokkal való munka során?

sobes.tech MI

Válasz az MI-től

A blokkoló szerkezetek, például a synchronized vagy a lock a standard könyvtárból, a korutinokkal való munka során az erőforrások nem hatékony kihasználásához vezethetnek, és problémákat okozhatnak.

Fő okok:

  • Szál blokkolása: A szinkronizációs blokkok megállítják a szál végrehajtását, nem csak a korutinokat. Ha egy Suspending függvény egy szinkronizált blokkban fut, akkor a szál blokkolva marad, amíg a Suspending függvény be nem fejeződik, és a zárolás fel nem oldódik. Ez más korutinoknak is lehetőséget ad arra, hogy ugyanazt a szálat használják, ami éhezést okozhat.
  • Kompatibilitás a Suspending függvényekkel: A korutinok működési mechanizmusa (környezetváltás, folytatások) nem veszi figyelembe a szál szintű zárolásokat. Egy Suspending függvény futtatása szinkronizált blokkban megsérti a korutinok nem blokkoló természetét.
  • A strukturált konkurencia elveinek megsértése: A korutinok saját leállítási és hierarchikus rendszerrel rendelkeznek. A szál szintű zárolások figyelmen kívül hagyják ezt a rendszert, megnehezítve a helyes leállításkezelést és az életciklus irányítását.
  • Deadlock kockázat: A szálak zárolásainak és a korutinok mechanizmusainak kombinációja növeli a komplex deadlockok kialakulásának valószínűségét, amelyek nehezen hibakereshetők.

A szál szintű zárolások helyett a kotlinx.coroutines könyvtárból származó speciális konkurencia primitíveket kell használni:

  • Mutex: Biztosítja a kölcsönös kizárást. Az a korutin, amely megpróbál egy foglalt mutexet megszerezni, szünetel (szál blokkolása nélkül) addig, amíg a mutex fel nem szabadul.
  • Semaphore: Korlátozza a párhuzamosan hozzáférő korutinok számát.
  • Channel: Adatok cseréjére használják a korutinok között.
  • Atomiás változók: Egyszerű atomikus műveletekhez.

A Mutex használatának példája:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.*

val mutex = Mutex()
var counter = 0

suspend fun incrementCounter() {
    mutex.withLock {
        // Kritikus szakasz
        counter++
    }
}

fun main() = runBlocking {
    // Több korutin létrehozása párhuzamos hozzáféréshez
    repeat(1000) {
        launch {
            incrementCounter()
        }
    }
    delay(1000) // Időt adunk a korutinoknak a futtatáshoz
    println("Counter: $counter") // Várható érték 1000
}

A mutex.withLock egy suspending-függvény. Ha a mutex foglalt, a withLock-ot hívó korutin szünetel, és felszabadítja a szálat más feladatok számára. Amikor a mutex elérhetővé válik, a korutin folytatódik. Ez nem blokkolja a szálat, ellentétben a synchronized-del.