Korzystając z GCD, chcemy przejść do wykonania i wykonania dwóch bloków asynchronicznych, zanim przejdziemy do kolejnych kroków wykonania. Jak najlepiej to zrobić?
Wypróbowaliśmy następujące, ale wydaje się, że to nie działa:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
});
// wait until both the block1 and block2 are done before start block3
// how to do that?
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
});
Odpowiedzi:
Użyj grup wysyłkowych: zobacz tutaj przykład „Oczekiwanie na grupy zadań w kolejce” w rozdziale „Kolejki wysyłkowe” w Przewodniku programowania współbieżności w bibliotece programistów iOS firmy Apple
Twój przykład może wyglądać mniej więcej tak:
i może produkować takie dane wyjściowe:
źródło
dispatch_group_async
jest jakdispatch_async
z dodanym parametrem grupy. Jeśli więc użyjesz różnych kolejek dla bloku 1 i bloku 2 lub zaplanujesz je w tej samej współbieżnej kolejce, mogą one działać jednocześnie; jeśli zaplanujesz je w tej samej kolejce szeregowej, będą one działać szeregowo. Nie różni się niczym od planowania bloków bez grup.Rozwijając odpowiedź Jörna Eyricha (głosuj na jego odpowiedź, jeśli ją zagłosujesz), jeśli nie masz kontroli nad
dispatch_async
wywołaniami bloków, tak jak w przypadku bloków asynchronicznych, możesz używać grup GCD bezpośredniodispatch_group_enter
idispatch_group_leave
bezpośrednio.W tym przykładzie udajemy, że
computeInBackground
nie możemy tego zmienić (wyobraź sobie, że jest to wywołanie zwrotne dla delegata, moduł obsługi zakończenia NSURLConnection, lub cokolwiek innego), a zatem nie mamy dostępu do wywołań wysyłki.W tym przykładzie computeInBackground: complete: jest zaimplementowany jako:
Dane wyjściowe (ze znacznikami czasu z przebiegu):
źródło
dispatch_queue_notify
prawdopodobnie jest to lepsze (chyba że czas blokowania jest gwarantowany na krótki).Dzięki Swift 5.1 Grand Central Dispatch oferuje wiele sposobów rozwiązania problemu. W zależności od potrzeb możesz wybrać jeden z siedmiu wzorów pokazanych w poniższych fragmentach placu zabaw.
# 1. Korzystanie
DispatchGroup
,DispatchGroup
'snotify(qos:flags:queue:execute:)
iDispatchQueue
' sasync(group:qos:flags:execute:)
Przewodnik programisty współbieżności dla programistów Apple mówi o
DispatchGroup
:# 2. Korzystanie
DispatchGroup
,DispatchGroup
'swait()
,DispatchGroup
' senter()
iDispatchGroup
„sleave()
Pamiętaj, że możesz także miksować
DispatchGroup
wait()
zDispatchQueue
async(group:qos:flags:execute:)
lub miksować zDispatchGroup
enter()
i .DispatchGroup
leave()
DispatchGroup
notify(qos:flags:queue:execute:)
# 3. Używanie i „s
DispatchWorkItemFlags
barrier
DispatchQueue
async(group:qos:flags:execute:)
Samouczek Grand Central Dispatch dla Swift 4: część 1/2 artykułu z Raywenderlich.com zawiera definicję barier :
Stosowanie:
# 4. Korzystanie
DispatchWorkItem
,DispatchWorkItemFlags
'sbarrier
iDispatchQueue
' sasync(execute:)
# 5. Korzystanie
DispatchSemaphore
,DispatchSemaphore
'swait()
iDispatchSemaphore
' ssignal()
Soroush Khanlou napisał następujące wiersze w poście na blogu The GCD Handbook :
Dokumentacja interfejsu API dla programistów Apple zawiera także następujące omówienie
DispatchSemaphore
init(value:)
inicjatora:Stosowanie:
# 6. Używanie
OperationQueue
iOperation
„saddDependency(_:)
Dokumentacja interfejsu API Apple Developer zawiera następujące informacje
OperationQueue
:Stosowanie:
# 7. Używanie
OperationQueue
iOperationQueue
„SaddBarrierBlock(_:)
(wymaga iOS 13)źródło
Inną alternatywą dla GCD jest bariera:
Po prostu utwórz współbieżną kolejkę, wyślij dwa bloki, a następnie wyślij ostatni blok z barierą, co sprawi, że będzie czekać na zakończenie pozostałych dwóch.
źródło
sleep()
! Dodałem tesleep()
wezwania tylko ze względów pedagogicznych, aby bloki działały wystarczająco długo, aby można było zobaczyć, że biegną one jednocześnie. W tym trywialnym przykładzie, przy brakusleep()
tych dwóch bloków, mogą one działać tak szybko, że wysłany blok może rozpocząć się i zakończyć, zanim będziesz miał szansę empirycznie obserwować równoczesne wykonanie. Ale niesleep()
wpisuj własnego kodu.Wiem, że pytałeś o GCD, ale jeśli chcesz,
NSOperationQueue
radzi sobie z tego rodzaju rzeczami naprawdę wdzięcznie, np .:źródło
NSOperation
podklasę, która jest współbieżna i ustawianaisFinished
po zakończeniu procesu asynchronicznego. Wtedy zależności działają dobrze.dispatch_semaphore_wait
nie odbywa się to w głównej kolejce i dopóki twoje sygnały i oczekiwania są zrównoważone). Tak długo, jak nie blokujesz głównej kolejki, podejście semaforowe jest w porządku, jeśli nie potrzebujesz elastyczności operacji (np. Mając możliwość anulowania ich, kontrolę stopnia współbieżności itp.).maxConcurrentOperationCount
na1
. Możesz także ustawić priorytet operacji, zarówno, jakqualityOfService
iqueuePriority
, ale mają one znacznie bardziej subtelny wpływ na priorytet zadania niż zależności i / lub stopień współbieżności kolejki.Wszystkie powyższe odpowiedzi są fajne, ale wszystkie przeoczyły jedną rzecz. grupa wykonuje zadania (bloki) w wątku, w którym wszedł, gdy używasz
dispatch_group_enter
/dispatch_group_leave
.działa to w utworzonej współbieżnej kolejce
demoQueue
. Jeśli nie utworzę żadnej kolejki, działa ona w głównym wątku .i istnieje trzeci sposób na wykonanie zadań w innym wątku:
Oczywiście, jak wspomniano, możesz użyć,
dispatch_group_async
aby uzyskać to, czego chcesz.źródło
Pierwsza odpowiedź jest zasadniczo poprawna, ale jeśli chcesz najprostszego sposobu osiągnięcia pożądanego rezultatu, oto samodzielny przykład kodu pokazujący, jak to zrobić za pomocą semafora (tak też działają grupy wysyłające za kulisami, JFYI) :
źródło
dispatch_semaphore_wait
. Masz dwa sygnały, więc potrzebujesz dwóch czekań. W tej chwili blok „uzupełniający” rozpocznie się, gdy pierwszy blok zasygnalizuje semafor, ale zanim zakończy się drugi blok; 2. Biorąc pod uwagę, że to pytanie dotyczyło systemu iOS, odradzam korzystanie z niegodispatch_main
.dispatch_semaphore_wait
Odblokuje jak tylko jedną zdispatch_semaphore_signal
metod są nazywane. Może się wydawać, że to działa, ponieważprintf
dla bloków „jeden” i „dwa” występują natychmiast, aprintf
dla „w końcu” następuje po odczekaniu - a więc po bloku spał przez 2 sekundy. Jeśli umieścisz printf posleep
wywołaniach, otrzymasz wynik dla „jeden”, następnie 2 sekundy później dla „w końcu”, a następnie 2 sekundy później dla „dwóch”.Szybka odpowiedź zaakceptowana:
źródło
Przykład Swift 4.2:
źródło
group.leave()
spowodował awarięNie wspominając, że inne odpowiedzi nie są świetne w pewnych okolicznościach, ale jest to jeden fragment, którego zawsze używam od Google:
źródło