Swift: O co chodzi z tymi kolejkami?

Nauka programownia

Opublikowany: Feb 17, 2019

Jeśli jesteście początkującymi programistami Swift i nie macie backgroundu w postaci nauki innych języków programowania, na pewno zastanowiło Was czym są wątki (threads) i kolejki (queues), o których otrzymaliście informację od Xcode w przypadku nieudanego wykonania jakiegoś fragmentu kodu.

OTO ŚPIESZĘ Z WYJAŚNIENIEM

Komputery (tak, iPhone i iPad to też komputery) już od jakiegoś czasu przestały przyspieszać. Tzn wciąż słyszym o procesorach taktowanych zegarami 2 i 3 gigaherce. A jeszcze kilkanaście lat temu podwojenie mocy procesorów następowało średnio raz na rok.

Brak zwiększania częstości zegara wynikł z pewnych ograniczeń fizycznych. Zamiast tego komputery otrzymały dostęp do wielu rdzeni procesorów. Znaczy to tyle, że fizycznie w procesorze jest dostępnych wiele podjednostek, które jednocześnie mogą prowadzić obliczenia.

I TU DOCHODZIMY DO WĄTKU WĄTKÓW

Żeby program mógł działać na takim procesorze, jego kod powinien być podzielony na takie fragmenty, które mogą wykonywać się równocześnie. Jakie to mogą być fragmenty? Nie trzbea długo szukać, żeby znaleźć przykłady czynności wykonywanych jednocecześnie: ś

  • Ściąganie pliku podczas przeglądania strony www.
  • Pobieranie piosenki podczas słuchania innej.
  • Planowanie drogi ucieczki antagonisty w grze podczas ataku gracza.
  • Mierzenie pulsu podczas sprawdzania godziny…

Ok. Część z tych przykładów dotyczy działania jednoczesnego różnyc aplikacji. Jednak idea pozostaje ta sama (chociaż uproszczona). Poszczególne czynności są wykonywane przez różne rdzenie procesora[1].

WĄTKI W PROGRAMOWANIU APLIKACJI DLA IOS

Jako programiści Swift możecie skorzystać z narzędzi tzw. Kolejek, które pomagają wykonywać czynności w innych wątkach. W takich kolejkach działających w tle powinniście uruchamiać wszystkie zadania, które wymagają trochę większych zasobów czasowych. Dzięki temu aplikacja będzie bardziej responsywna (czytaj: będzie działała lepiej i płynniej i więcej ludzi ją kupi).

Jak to zrobić?

Jest to bardzo proste. Kod, który ma wykonywać się w tle należy przekazać do odpowiedniej kolejki

let cQ = DispatchQueue(label: "nazwa")

Obiekt kolejki tworzony jest za pomocą konstrkutkra DispatchQueue. Taka kolejka jest domyślnie synchroniczna. To znaczy, że wszystkie operacje, które będą miały wykonywać się w niej asynchronicznie (niezależnie od głównego wątku) będą w niej wykonane po kolei.

Spróbujmy przekazać do tak stworzonej kolejki jakieś operacje. Jej działanie dobrze widać na przykładach pętli wypisujących coś na standardowe wyjście.

print("PRZED PRZEKAZANIEM ZADAŃ DO KOLEJKI")

cQ.sync {
    for i in 1 ... 10  {
        print("\(i) ++++")
    }
}

cQ.sync {
    for i in 1 ... 10  {
        print("\(i) ----")
    }
}
print("JUŻ PO PRZEKAZANIU ZADAŃ DO KOLEJKI")

Wywołanie synchroniczne spowodowało, że wynik jest łatwy do przewidzenia

PRZED PRZEKAZANIEM ZADAŃ DO KOLEJKI
1 ++++
2 ++++
3 ++++
4 ++++
5 ++++
1 ----
2 ----
3 ----
4 ----
5 ----

JUŻ PO PRZEKAZANIU ZADAŃ DO KOLEJKI

Widać, że kod, który jest poza nową kolejką (czyli linia wypisywana przed i po przekazaniu zadań do kolejki jest wykonana zgodnie ze spodziewaną kolejnością. Zatem główny fragment kodu czekał, aż zadania pętli się zakończą.

Jeśli zmienimy typ wywołania na async, okaże się, że zadania z pętli zaczną żyć własnym życiem, a wywołanie async nie spowoduje zablokowania głównego wątku.

PRZED PRZEKAZANIEM ZADAŃ DO KOLEJKI
1 ++++
2 ++++
JUŻ PO PRZEKAZANIU ZADAŃ DO KOLEJKI
3 ++++
4 ++++
5 ++++
1 ----
2 ----
3 ----
4 ----
5 ----

Jeszcze ciekawiej będzie, gdy pozwolimy obu zadaniom być realizowanym jednocześnie, zmieniając atrybuty nowo utworzonej kolejki.

let cQ = DispatchQueue(label: "nazwa",  attributes: .concurrent)

Wówczas pętle w wywołaniach ansyhcronicznych będą przenikały się, generując taki oto wynik.

PRZED PRZEKAZANIEM ZADAŃ DO KOLEJKI
1 ----
JUŻ PO PRZEKAZANIU ZADAŃ DO KOLEJKI
1 ++++
2 ----
3 ----
4 ----
2 ++++
5 ----
3 ++++
4 ++++
5 ++++

Ale co zrobić, gdybyśmy chcieli wiedzieć, kiedy instrukcji z kolejki zakończą się? Trzeba wrócić wtedy do głównego wątku i na przykład wywołać jakąś funkcję. W treści kodu przekazanego do funkcji async dodajemy więc:

DispatchQueue.main.sync {
        //Kod do wykonania w wątku głównym
    }

A w bloku sync możemy już zmodyfikować UI, lub wykonać kod odpowiedzialny za zagospodarowanie wyników uzyskanych w wątku w tle.

CZY TO WSZYSTKO?

Ten przykład nie wyczerpuje możliwości wątków i kolejek, a w szczególności pomija podstawowe aspekty synchronizacji tychże. Dużo więc jeszcze jest do odkrycia! Powodzenia!


dr Błażej Zyglarski

Autoryzowany Trener Apple (Swift), praktyk z wieloletnim doświadczeniem

Od lat pasjonat technologii mobilnych. Autor dziesiątek aplikacji dla systemów iOS, tvOS i watchOS. Wykładowca na Wydziale Matematyki i Informatyki Uniwersytetu Mikołaja Kopernika w Toruniu. Praktykujący deweloper iOS. Współzałożyciel Asuri Solutions.


Image
Programowanie Swift
Swift on Windows
Sep 23, 2020
Image
Felieton
Zarabianie Schrodingera
Sep 03, 2019
Image
Felieton
Rozliczanie aplikacji sprzedanych w AppStore
Jun 10, 2019
Image
Felieton
Cały internet na płycie CD
Apr 01, 2019
Image
Nauka programownia
Swift: O co chodzi z tymi kolejkami?
Feb 17, 2019

Nasze szkolenia