Zrozumienie dispatch_async


233

Mam pytanie dotyczące tego kodu

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

Pierwszy parametr tego kodu to

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Czy pytamy ten kod o wykonywanie zadań szeregowych w globalnej kolejce, której sama definicja polega na tym, że zwraca globalną kolejkę współbieżną o danym poziomie priorytetu?

Jaka jest zaleta korzystania dispatch_get_global_queuez głównej kolejki?

Jestem zdezorientowany. Czy możesz mi pomóc lepiej to zrozumieć.


1
Lepiej wytnij kod w kilka wierszy, aby miało to większy sens. zabezpieczyć dispatch_get_global_queuewnętrze różnego rodzaju dispatch_queue_t myQueue. Bardziej czytelne jest przekazywanie tylko myQueue do twojego `` dispatch_async``
Alex Cio

Odpowiedzi:


517

Głównym powodem używania kolejki domyślnej w kolejce głównej jest uruchamianie zadań w tle.

Na przykład, jeśli pobieram plik z Internetu i chcę zaktualizować użytkownika o postępie pobierania, uruchomię pobieranie w domyślnej kolejce priorytetowej i asynchronicznie zaktualizuję interfejs użytkownika w głównej kolejce.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

Rozumiem, że David podziękował za twoją odpowiedź, ale moje pytanie dotyczyło bardziej logiki zrobienia tego, tj.
Poproszenia

Robię dokładnie to, co sugerujesz, ale jakoś uiTableViewCell nie aktualizuje od razu, kiedy wywołuję [self.tableView reloadData] w Aktualizacjach UI. Zajmuje to około 4-5 sekund. Doprowadza mnie to do szału od kilku dni .
GrandSteph

@GrandSteph Nie znam tej metody zbyt dobrze. Być może uruchomienie tej metody zajmuje tylko 5 sekund. Ważną rzeczą w dispatch_async jest to, że pozwala robić rzeczy w tle bez zawieszania głównego wątku.
David

2
co 0oznacza
Honey,

3
@ Kochanie 0 to flagsparametr, który obecnie nie robi absolutnie nic. Z dokumentów:Flags that are reserved for future use. Always specify 0 for this parameter.
David

199

Wszystkie kolejki DISPATCH_QUEUE_PRIORITY_X są kolejkami równoczesnymi (co oznacza, że ​​mogą wykonywać wiele zadań jednocześnie) i są FIFO w tym sensie, że zadania w danej kolejce zaczną być wykonywane w kolejności „pierwsze przyszło, pierwsze wyszło”. Jest to w porównaniu z kolejką główną (z dispatch_get_main_queue ()), która jest kolejką szeregową (zadania zaczną się kończyć i kończą się w kolejności, w jakiej zostały odebrane).

Tak więc, jeśli wyślesz 1000 bloków dispatch_async () do DISPATCH_QUEUE_PRIORITY_DEFAULT, zadania te zaczną być wykonywane w kolejności, w jakiej je wysłałeś do kolejki. Podobnie w przypadku kolejek WYSOKIE, NISKIE i TŁO. Wszystko, co wysyłasz do którejkolwiek z tych kolejek, jest wykonywane w tle w alternatywnych wątkach, z dala od głównego wątku aplikacji. Dlatego te kolejki są odpowiednie do wykonywania zadań, takich jak pobieranie w tle, kompresja, obliczenia itp.

Należy pamiętać, że kolejność wykonywania jest FIFO na podstawie kolejki. Jeśli więc wyślesz 1000 zadań dispatch_async () do czterech różnych równoczesnych kolejek, równomiernie je dzieląc i wysyłając do BACKGROUND, LOW, DEFAULT i HIGH w kolejności (tj. Planujesz ostatnie 250 zadań w kolejce HIGH), jest bardzo prawdopodobne, że pierwsze zadania, które zaczniesz, będą w tej kolejce WYSOKIEJ, ponieważ system przyjął twoją sugestię, że zadania te muszą dotrzeć do procesora tak szybko, jak to możliwe.

Zauważ też, że mówię „zacznie wykonywać w kolejności”, ale pamiętaj, że w przypadku równoczesnych kolejek rzeczy niekoniecznie kończą wykonywanie w kolejności, w zależności od długości czasu dla każdego zadania.

Zgodnie z Apple:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Współbieżna kolejka wysyłkowa jest przydatna, gdy masz wiele zadań, które można uruchamiać równolegle. Współbieżna kolejka jest nadal kolejką, ponieważ usuwa kolejność zadań w kolejności „pierwsze przyszło, pierwsze wyszło”; jednak współbieżna kolejka może anulować kolejność zadań dodatkowych przed zakończeniem jakichkolwiek poprzednich zadań. Rzeczywista liczba zadań wykonywanych przez współbieżną kolejkę w danym momencie jest zmienna i może zmieniać się dynamicznie wraz ze zmianami warunków w aplikacji. Wiele czynników wpływa na liczbę zadań wykonywanych przez współbieżne kolejki, w tym liczbę dostępnych rdzeni, ilość pracy wykonanej przez inne procesy oraz liczbę i priorytet zadań w innych kolejkach wysyłki szeregowej.

Zasadniczo, jeśli wyślesz te 1000 bloków dispatch_async () do kolejki DEFAULT, HIGH, LOW lub BACKGROUND, wszystkie zaczną działać w kolejności, w jakiej je wysyłasz. Jednak krótsze zadania mogą zakończyć się przed dłuższymi. Powodem tego są dostępne rdzenie procesora lub bieżące zadania w kolejce wykonują obliczeniowo niewymagające intensywności prace (co sprawia, że ​​system myśli, że może równolegle wykonywać dodatkowe zadania niezależnie od liczby rdzeni).

Poziom współbieżności jest w całości obsługiwany przez system i jest oparty na obciążeniu systemu i innych wewnętrznych czynnikach. To jest piękno Grand Central Dispatch (system dispatch_async ()) - po prostu tworzysz swoje jednostki robocze jako bloki kodu, ustawiasz dla nich priorytet (na podstawie wybranej przez ciebie kolejki) i pozwalasz systemowi zająć się resztą.

Tak więc, aby odpowiedzieć na powyższe pytanie: masz częściowo rację. „Pytasz ten kod” o wykonywanie równoczesnych zadań w globalnej kolejce współbieżnej na określonym poziomie priorytetu. Kod w bloku będzie wykonywany w tle, a każdy dodatkowy (podobny) kod będzie wykonywany potencjalnie równolegle, w zależności od oceny dostępnych zasobów przez system.

Z kolei „główna” kolejka (z dispatch_get_main_queue ()) jest kolejką szeregową (nie współbieżną). Zadania wysyłane do kolejki głównej będą zawsze wykonywane w kolejności i zawsze kończą się w kolejności. Te zadania będą również wykonywane w wątku interfejsu użytkownika, więc nadaje się do aktualizowania interfejsu użytkownika za pomocą komunikatów o postępach, powiadomień o zakończeniu itp.


+1, ale myślę, że w praktyce nie ma większego znaczenia, czy współbieżne kolejki to FIFO, czy tylko kolejność losowa. Jeśli rozpoczniesz 5 zadań w pętli, załóż, że w zasadzie zaczną się w tym samym czasie. Nie ma gwarancji, że np. Pierwsza operacja we / wy pierwszego zadania nastąpi przed piątym, nawet jeśli wykonają ten sam kod. OTOH, w przypadku kolejek szeregowych kluczowe znaczenie ma zachowanie FIFO, a IMHO to różnica definiująca oba typy kolejek.
Gerhard Wesp,

Niesamowite wyjaśnienie. Dużo klaszcze!
Okhan Okbay

36

Wersja szybka

To jest szybka wersja odpowiedzi Objective-C Davida. Za pomocą globalnej kolejki można uruchamiać rzeczy w tle, a kolejki głównej aktualizować interfejs użytkownika.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.