Middle+
Jaki jest najbardziej wyraźny przykład deadlocka w iOS, który możesz podać?
sobes.tech AI
Odpowiedź od AI
Najbardziej oczywisty przykład deadlocka w iOS występuje podczas próby aktualizacji interfejsu użytkownika z wątku w tle bez użycia specjalnych mechanizmów.
// Ten kod spowoduje deadlock lub awarię (w zależności od wersji iOS i kontekstu)
// przy wywołaniu z wątku nie będącego głównym
DispatchQueue.global().async {
// Wykonujemy pewną pracę w tle...
print("Praca w tle w toku")
// Próbujemy zaktualizować UI bezpośrednio z wątku w tle
// To spowoduje wyjątek lub deadlock, ponieważ UIKit nie jest bezpieczny dla wątków
DispatchQueue.main.syncが必要です!
// To zostało dodane, aby pokazać problem, nie należy tak robić w rzeczywistym kodzie!
DispatchQueue.main.sync {
// Próba zaktualizowania etykiety na głównym wątku
// Ale ponieważ już oczekujemy synchronicznie na główny wątek, który jest zablokowany
// przez nasz własny blok wysyłki synchronicznej, dochodzi do deadlocka.
print("Próba aktualizacji UI")
// someLabel.text = "Zaktualizowano z tła" // Przykład linii, która spowodowałaby problem
}
print("Praca w tle zakończona")
}
Wyjaśnienie:
UIKit (framework do tworzenia interfejsów użytkownika w iOS) nie jest bezpieczny dla wątków. Wszystkie operacje na UI muszą być wykonywane ściśle na głównym wątku (main thread).
W powyższym przykładzie, jeśli wywołasz blok DispatchQueue.main.sync { ... } z wątku w tle, to:
- Wątek w tle zostanie zablokowany i będzie czekał, aż główny wątek wykona przesłany mu blok.
- Główny wątek może być zajęty lub, co bardziej prawdopodobne w tym scenariuszu, oczekuje na zakończenie operacji, która powinna zostać uruchomiona z wątku w tle (chociaż w tym konkretnym przykładzie tak nie jest, wysyłanie synchroniczne na główny wątek z wątku w tle jest już problematyczne).
- W przypadku
sync, wątek w tle nie może zakończyć pracy, dopóki główny wątek nie wykona przesłanego bloku. Jeśli główny wątek jest już zablokowany (np. oczekuje na zakończenie operacji w wątku w tle, który z kolei oczekuje na główny wątek, lub jest po prostu zajęty), dochodzi do cyklicznego oczekiwania, czyli deadlocka.
Poprawne rozwiązanie:
Użyj asynchronicznego wysyłania na główny wątek do aktualizacji UI.
// Poprawne rozwiązanie: aktualizacja UI asynchronicznie na głównym wątku
DispatchQueue.global().async {
// Wykonujemy pewną pracę w tle...
print("Praca w tle w toku")
// Wysyłamy aktualizację UI asynchronicznie na główny wątek
DispatchQueue.main.async {
print("Aktualizacja UI na głównym wątku")
// someLabel.text = "Zaktualizowano z tła" // Miejsce do aktualizacji UI
}
print("Praca w tle zakończona")
}
Tutaj, wątek w tle nie jest blokowany podczas wysyłania bloku na główny wątek, co pozwala zarówno zadaniu w tle, jak i głównemu wątkowi działać niezależnie, unikając deadlocka.