Różnica między DispatchQueue.main.async i DispatchQueue.main.sync

109

Od dawna używam DispatchQueue.main.asyncdo wykonywania operacji związanych z interfejsem użytkownika.



Swift zapewnia zarówno DispatchQueue.main.asynci DispatchQueue.main.sync, jak i oba są wykonywane w głównej kolejce.



Czy ktoś może mi powiedzieć, jaka jest między nimi różnica? Kiedy powinienem używać każdego?



DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}
Aman.Samghani
źródło

Odpowiedzi:

56

Kiedy asyncgo używasz , pozwala kolejce wywoływania przejść bez czekania, aż wysłany blok zostanie wykonany. Wręcz przeciwnie sync, zatrzyma kolejkę wywołań i zaczeka, aż praca, którą wysłałeś w bloku, zostanie wykonana. Dlatego syncmoże prowadzić do impasu. Spróbuj uruchomić DispatchQueue.main.syncz głównej kolejki, a aplikacja zawiesi się, ponieważ kolejka wywołań będzie czekać, aż wysłany blok się skończy, ale nie będzie nawet w stanie się uruchomić (ponieważ kolejka jest zatrzymana i czeka)

Kiedy używać sync? Gdy musisz poczekać na wykonanie czynności w INNEJ kolejce i dopiero wtedy kontynuować pracę nad bieżącą kolejką

Przykład użycia synchronizacji:

W kolejce szeregowej możesz użyć syncjako muteksu, aby upewnić się, że tylko jeden wątek jest w stanie wykonać chroniony fragment kodu w tym samym czasie.

Andrey Chernukha
źródło
Czy byłoby źle dzwonić DispatchQueue.main.syncz wątku w tle?
Honey
@ Kochanie Generalnie nie ma nic złego w takim połączeniu (o ile główna kolejka nie robi nic ciężkiego i czasochłonnego), ale w praktyce nie przychodzi mi do głowy sytuacja, w której naprawdę tego potrzebujesz. Zdecydowanie powinno być lepsze rozwiązanie
Andrey Chernukha
1
@Honey Jedną z takich sytuacji jest aktualizacja CollectionView of PHAssets z PhotoKit API, jak pokazano w dokumentacji tutaj: developer.apple.com/documentation/photokit/ ...
filiżanka do herbaty
1
@teacup interesujące. Zastanawiam się tylko, jak by to było inaczej, gdybyśmy asynctam zadzwonili ? Chodzi mi o to, że skoro w wątku nie ma nic innego, to nie ma znaczenia. Gdyby tak było, DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};to miałoby to sens. Ale kiedy nie ma innego bloku, nie mogę myśleć o korzyściach z używania DispatchQueue.main.sync {Oneblock}go DispatchQueue.main.async {Oneblock}. Dla obu z nich otrzymają priorytet / natychmiastowość mainQueue i nic im nie przeszkodzi.
Honey
3
@ Kochanie „ponieważ nie ma nic więcej w wątku później” nie jest prawdą, gdy jesteś w głównym wątku, który jest odpowiedzialny za obsługę wszystkich interakcji użytkownika z aplikacją. Na przykład użytkownik może usunąć inne zdjęcie, zanim photoLibraryDidChange zwróci ze zaktualizowanym źródłem danych, powodując krytyczny błąd niespójności.
filiżanka do herbaty
166

Dlaczego współbieżność?

Gdy tylko dodasz do aplikacji ciężkie zadania, takie jak ładowanie danych, spowalnia ona pracę interfejsu użytkownika lub nawet ją zawiesza. Współbieżność umożliwia wykonywanie 2 lub więcej zadań „jednocześnie”. Wadą tego podejścia jest bezpieczeństwo gwintów, które nie zawsze jest tak łatwe do kontrolowania. Na przykład, gdy różne zadania chcą uzyskać dostęp do tych samych zasobów, np. Próba zmiany tej samej zmiennej w różnych wątkach lub uzyskiwanie dostępu do zasobów już zablokowanych przez różne wątki.

Jest kilka abstrakcji, o których musimy wiedzieć.

  • Kolejki.
  • Synchroniczna / asynchroniczna wydajność zadań.
  • Priorytety.
  • Typowe problemy.

Kolejki

Musi być seryjny lub równoległy . A także globalne lub prywatne w tym samym czasie.

W przypadku kolejek szeregowych zadania będą kończone pojedynczo, podczas gdy w przypadku kolejek współbieżnych zadania będą wykonywane jednocześnie i będą kończone zgodnie z nieoczekiwanymi harmonogramami. Ta sama grupa zadań zajmie znacznie więcej czasu w kolejce szeregowej w porównaniu z kolejką współbieżną.

Możesz tworzyć własne kolejki prywatne (zarówno szeregowe, jak i współbieżne ) lub korzystać z już dostępnych kolejek globalnych (systemowych) . Kolejka główny jest jedynym seryjnym kolejka z wszystkich globalnych kolejek .

Zdecydowanie zaleca się, aby nie wykonywać ciężkich zadań, które nie są związane z pracą interfejsu użytkownika w głównej kolejce (np. Ładowanie danych z sieci), ale zamiast tego wykonywać je w innych kolejkach, aby interfejs użytkownika nie był zamrażany i reagował na działania użytkownika. Jeśli pozwolimy zmienić interfejs użytkownika w innych kolejkach, zmiany mogą zostać wprowadzone w innym i nieoczekiwanym harmonogramie i szybkości. Niektóre elementy interfejsu użytkownika można narysować przed lub po ich pojawieniu się. Może to spowodować awarię interfejsu użytkownika. Musimy również pamiętać, że skoro kolejki globalnekolejkami systemowymi, system może na nich wykonywać inne zadania.

Jakość usług / priorytet

Kolejki mają również różne qos (Quality of Service), które ustawiają priorytet zadania (od najwyższego do najniższego):
.userInteractive - kolejka główna
.userInitiated - dla zadań inicjowanych przez użytkownika, na które użytkownik czeka na odpowiedź.
Użyteczność - dla zadań co zajmuje trochę czasu i nie wymaga natychmiastowej reakcji, np. praca z danymi
.background - dla zadań niezwiązanych z częścią wizualną, które nie są ściśle związane z czasem realizacji).

Istnieje również kolejka

.default, która nie przenosi informacji qos . Jeśli nie można było wykryć qos, toqos będzie używany między .userInitiated i .utility .

Zadania można wykonywać synchronicznie lub asynchronicznie .

  • Funkcja synchroniczna przywraca sterowanie do bieżącej kolejki dopiero po zakończeniu zadania. Blokuje kolejkę i czeka na zakończenie zadania.

  • Funkcja asynchroniczna zwraca sterowanie do bieżącej kolejki zaraz po wysłaniu zadania do wykonania w innej kolejce. Nie czeka na zakończenie zadania. Nie blokuje kolejki.

Typowe problemy.

Oto najpopularniejsze błędy popełniane przez programistów podczas projektowania aplikacji współbieżnych:

  • Stan wyścigu - występuje, gdy działanie aplikacji zależy od kolejności wykonywania części kodu.
  • Odwrócenie priorytetów - gdy zadania o wyższym priorytecie czekają na zakończenie zadań o mniejszym priorytecie z powodu zablokowania niektórych zasobów
  • Deadlock - gdy kilka kolejek ma nieskończone oczekiwanie na źródła (zmienne, dane itp.) Już zablokowane przez niektóre z tych kolejek.

NIGDY nie wywołuj funkcji synchronizacji w głównej kolejce .
Jeśli wywołasz funkcję synchronizacji w głównej kolejce, zablokuje ona kolejkę, a kolejka będzie czekała na zakończenie zadania, ale zadanie nigdy nie zostanie ukończone, ponieważ nie będzie można go nawet uruchomić, ponieważ kolejka jest już zablokowane. Nazywa się to impasem .

Kiedy używać synchronizacji? Kiedy musimy poczekać, aż zadanie zostanie zakończone. Fe, gdy upewniamy się, że jakaś funkcja / metoda nie jest wywoływana podwójnie. Mamy synchronizację i próbujemy zapobiec podwójnemu wywołaniu, dopóki nie zostanie całkowicie zakończona. Oto kod dotyczący tego problemu:
Jak dowiedzieć się, co spowodowało raport o awarii urządzenia z systemem IOS?

Aleksandra
źródło
3
Myślę, że „NIGDY nie wywołuj funkcji synchronizacji w głównej kolejce” jest słuszne. Istnieją przypadki, w których wywołujesz synchronizację w głównym wątku, na przykład gdy masz globalny licznik, którego potrzebujesz, aby każdy obiekt używał i zwiększał: dispatchQueue.sync {count + = 1; self.orderId = count}
Elisha Sterngold
6
Klasa QOS - .userInteractive NIE jest kolejką główną.
Kunal Shah
1
Czy byłoby źle dzwonić DispatchQueue.main.syncz wątku w tle?
Honey
1
@ Kochanie, nie, to nie jest złe nazywanie tego, ale z mojego doświadczenia wynika, że ​​wywołasz więcej DispatchQueue.main.async poza sync.
James Kim
2
Czy nie byłoby dokładniejsze stwierdzenie, że nie należy nigdy wywoływać funkcji sync () w bieżącej kolejce? Nie jest źle wywoływać sync () w głównej kolejce, jeśli jesteś w innej kolejce, jeśli dobrze rozumiem.
ok
1

GCDumożliwia wykonanie zadania synchronouslylub asynchronously[Informacje]

synchronous(blokuj i czekaj) zwraca kontrolę, kiedy zadanie zostanie zakończone

asynchronousFunkcja (wysyłka i kontynuacja) natychmiast zwraca formant, wysyłając zadanie do uruchomienia do odpowiedniej kolejki, ale nie czekając na jego zakończenie.

[DispatchQueue]

yoAlex5
źródło
0

synclub asyncmetody nie mają wpływu na kolejkę, w której są wywoływane.

synczablokuje wątek, z którego jest wywoływany, a nie kolejkę, w której jest wywoływany. Jest to właściwość, DispatchQueuektóra decyduje o tym, czy DispatchQueuebędzie czekał na wykonanie zadania (kolejka szeregowa), czy może uruchomić kolejne zadanie przed zakończeniem bieżącego zadania (kolejka współbieżna).

Więc nawet jeśli DispatchQueue.main.asyncjest to wywołanie asynchroniczne, dodana do niego ciężka operacja może spowodować zawieszenie interfejsu użytkownika, ponieważ jej operacje są wykonywane szeregowo w głównym wątku. Jeśli ta metoda jest wywoływana z wątku w tle, sterowanie wróci do tego wątku natychmiastowo, nawet jeśli interfejs użytkownika wydaje się być zablokowany. Dzieje się tak, ponieważ asyncnawiązywane jest połączenieDispatchQueue.main

Vipin
źródło