Sobes.tech
Middle+

Jaký je nejvýraznější příklad deadlocku v iOS, který můžete uvést?

sobes.tech AI

Odpověď od AI

Nejjasnější příklad deadlocku v iOS nastává při pokusu aktualizovat UI z pozadí bez použití speciálních mechanismů.

// Tento kód způsobí deadlock nebo pád (v závislosti na verzi iOS a kontextu)
// při volání mimo hlavní vlákno
DispatchQueue.global().async {
    // Provádíme nějakou práci na pozadí...
    print("Práce na pozadí probíhá")

    // Pokusíme se aktualizovat UI přímo z pozadí
    // To způsobí výjimku nebo deadlock, protože UIKit není vlákno-bezpečný
    DispatchQueue.main.syncが必要です!
    // Toto je přidáno pro demonstraci problému - v reálném kódu to dělat nelze!
    DispatchQueue.main.sync {
        // Pokus o aktualizaci štítku na hlavním vlákně
        // Ale protože již synchronně čekáme na hlavní vlákno, které je zablokované
        // naším vlastním synchronním blokem odeslání, vzniká deadlock.
        print("Pokoušíme se aktualizovat UI")
        // someLabel.text = "Aktualizováno z pozadí" // Příklad řetězce, který by způsobil problém
    }
    print("Práce na pozadí dokončena")
}

Vysvětlení:

UIKit (framework pro tvorbu uživatelského rozhraní v iOS) není vlákno-bezpečný. Všechny operace s UI musí být prováděny striktně na hlavním vlákně (main thread).

V uvedeném příkladu, pokud zavoláte blok DispatchQueue.main.sync { ... } z pozadí, pak:

  1. Pozadí vlákno se zablokuje a čeká, až hlavní vlákno provede odeslaný blok kódu.
  2. Hlavní vlákno může být zaneprázdněné, nebo, což je pravděpodobnější v tomto scénáři, samo čeká na dokončení nějaké operace, která měla být spuštěna z pozadí (ačkoliv v tomto konkrétním příkladu toho není, synchronní odeslání na hlavní vlákno z pozadí je problémem).
  3. V případě sync se pozadí vlákno nemůže dokončit, dokud hlavní vlákno ne provede předaný blok. Pokud je hlavní vlákno již zablokované (například čeká na dokončení operace v pozadí, která zase čeká na hlavní vlákno, nebo je prostě zaneprázdněné), vzniká cyklické čekání, tedy deadlock.

Správné řešení:

Použít asynchronní odeslání na hlavní vlákno pro aktualizaci UI.

// Správné řešení: aktualizace UI asynchronně na hlavním vlákně
DispatchQueue.global().async {
    // Provádíme nějakou práci na pozadí...
    print("Práce na pozadí probíhá")

    // Odesíláme aktualizaci UI asynchronně na hlavní vlákno
    DispatchQueue.main.async {
        print("Aktualizujeme UI na hlavním vlákně")
        // someLabel.text = "Aktualizováno z pozadí" // Správné místo pro aktualizaci UI
    }
    print("Práce na pozadí dokončena")
}

Zde pozadí vlákno není zablokované při odeslání bloku na hlavní vlákno, což umožňuje jak pozadí, tak hlavnímu vláknu pracovat nezávisle, čímž se vyhýbáme deadlocku.