Sobes.tech
Middle+

Mi a legfeltűnőbb deadlock példa az iOS-ben, amit meg tudna nevezni?

sobes.tech MI

Válasz az MI-től

A legnyilvánvalóbb példa a deadlock-ra az iOS-ben akkor fordul elő, amikor megpróbálod frissíteni a UI-t háttérszálról anélkül, hogy speciális mechanizmusokat használnál.

// Ez a kód deadlock-ot vagy összeomlást okoz (az iOS verziójától és a kontextustól függően)
// amikor nem fő szálról hívják
DispatchQueue.global().async {
    // Valamilyen háttérmunkát végzünk...
    print("Háttérmunka folyamatban")

    // Megpróbáljuk közvetlenül frissíteni a UI-t a háttérszálról
    // Ez kivételt vagy deadlock-ot okoz, mivel a UIKit nem szálbiztos
    DispatchQueue.main.syncが必要です!
    // Ez hozzá lett adva a probléma demonstrálására, ne csináld ezt valódi kódban!
    DispatchQueue.main.sync {
        // Próbálkozás egy címke frissítésével a fő szálon
        // De mivel már szinkron módon várunk a fő szálra, ami blokkolva van
        // saját szinkron küldő blokkunk által, deadlock keletkezik.
        print("UI frissítése próbálkozás")
        // someLabel.text = "Háttérről frissítve" // Példasor, ami problémát okozna
    }
    print("Háttérmunka befejeződött")
}

Magyarázat:

A UIKit (az iOS felhasználói felületének építésére szolgáló keretrendszer) nem szálbiztos. Minden UI műveletet szigorúan a fő szálon kell végrehajtani.

A fenti példában, ha a DispatchQueue.main.sync { ... } blokkot háttérszálról hívod, akkor:

  1. A háttérszál blokkolódik, és vár, hogy a fő szál végrehajtsa a neki küldött blokkot.
  2. A fő szál lehet, hogy elfoglalt vagy, vagy, ami valószínűbb ebben a szcenárióban, vár egy olyan művelet befejezésére, amit a háttérszálról kellett volna indítani (bár ebben a példában nem így van, a szinkron küldés a fő szálra már problémás).
  3. A sync esetén a háttérszál nem tud befejezni, amíg a fő szál nem hajtja végre a küldött blokkot. Ha a fő szál már blokkolva van (például egy háttérben futó művelet befejezését várja, ami viszont a fő szálat várja, vagy egyszerűen elfoglalt), ciklikus várakozás, azaz deadlock keletkezik.

Helyes megoldás:

Használj aszinkron küldést a fő szálra a UI frissítéséhez.

// Helyes megoldás: aszinkron UI frissítés a fő szálon
DispatchQueue.global().async {
    // Valamilyen háttérmunkát végzünk...
    print("Háttérmunka folyamatban")

    // Aszinkron küldés a fő szálra a UI frissítéséhez
    DispatchQueue.main.async {
        print("UI frissítése a fő szálon")
        // someLabel.text = "Háttérről frissítve" // Hely a UI frissítéséhez
    }
    print("Háttérmunka befejeződött")
}

Ebben az esetben a háttérszál nem blokkolódik a blokk küldése közben a fő szálra, lehetővé téve mind a háttérfeladat, mind a fő szál független működését, így elkerülve a deadlock-ot.