dispatch_sync
robi dwie rzeczy:
- kolejkę bloku
- blokuje obecny wątek, aż blok zakończył prowadzenie
Zważywszy, że główny wątek jest kolejką seryjny (co oznacza używa tylko jednego wątku), następująca instrukcja:
dispatch_sync(dispatch_get_main_queue(), ^(){/*...*/});
powoduje następujące zdarzenia:
dispatch_sync
kolejek bloku w głównym kolejce.
dispatch_sync
blokuje wątek głównej kolejki, aż blok zostanie zakończony.
dispatch_sync
czeka na zawsze, ponieważ wątek, w którym blok ma działać, jest zablokowany.
Kluczem do zrozumienia tego jest to, że dispatch_sync
nie wykonuje bloków, tylko je umieszcza w kolejce. Wykonanie nastąpi przy kolejnej iteracji pętli uruchamiania.
następujące podejście:
if (queueA == dispatch_get_current_queue()){
block();
} else {
dispatch_sync(queueA,block);
}
jest w porządku, ale należy pamiętać, że nie będzie cię chronić od złożonych scenariuszy obejmujących hierarchię kolejek. W takim przypadku bieżąca kolejka może być inna niż poprzednio zablokowana kolejka, w której próbujesz wysłać swój blok. Przykład:
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
// dispatch_get_current_queue() is B, but A is blocked,
// so a dispatch_sync(A,b) will deadlock.
dispatch_sync(queueA, ^{
// some task
});
});
});
przypadku skomplikowanych przypadkach, odczyt/zapis danych klucz-wartość w kolejce wysyłkowy:
dispatch_queue_t workerQ = dispatch_queue_create("com.meh.sometask", NULL);
dispatch_queue_t funnelQ = dispatch_queue_create("com.meh.funnel", NULL);
dispatch_set_target_queue(workerQ,funnelQ);
static int kKey;
// saves string "funnel" in funnelQ
CFStringRef tag = CFSTR("funnel");
dispatch_queue_set_specific(funnelQ,
&kKey,
(void*)tag,
(dispatch_function_t)CFRelease);
dispatch_sync(workerQ, ^{
// is funnelQ in the hierarchy of workerQ?
CFStringRef tag = dispatch_get_specific(&kKey);
if (tag){
dispatch_sync(funnelQ, ^{
// some task
});
} else {
// some task
}
});
Objaśnienie:
- tworzę
workerQ
kolejkę wskazujący na funnelQ
kolejka. W prawdziwym kodzie jest to użyteczne, jeśli masz kilka kolejek "pracowniczych" i chcesz wznowić/zawiesić wszystkie naraz (co jest osiągane przez wznowienie/aktualizację kolejki docelowej).
- Mogę kierować kolejkami pracowniczymi w dowolnym momencie, więc aby się dowiedzieć, czy są one przekazywane, czy nie, oznaczam tag
funnelQ
słowem "lejek".
- dół drogi I
dispatch_sync
coś workerQ
, i dla jakiegoś powodu chcę dispatch_sync
do funnelQ
, ale unikając dispatch_sync do bieżącej kolejki, więc sprawdzić dla tagu i podjąć odpowiednie działania. Ponieważ get idzie w górę hierarchii, wartość nie zostanie znaleziona w workerQ
, ale zostanie znaleziona w funnelQ
. Jest to sposób sprawdzenia, czy jakakolwiek kolejka w hierarchii jest tą, w której zapisaliśmy tę wartość. I dlatego, aby zapobiec dispatch_sync do bieżącej kolejki.
Jeśli zastanawiasz się o funkcjach, które odczytują dane/Context zapisu, są trzy:
dispatch_queue_set_specific
: Napisz do kolejki.
dispatch_queue_get_specific
: Czytaj z kolejki.
dispatch_get_specific
: Wygodna funkcja do odczytu z bieżącej kolejki.
Klucz jest porównywany przez wskaźnik i nigdy nie jest dereferencjonowany. Ostatnim parametrem w ustawniku jest destruktor, który zwolni klucz.
Jeśli zastanawiasz się nad "wskazaniem jednej kolejki do drugiej", oznacza to dokładnie to. Na przykład mogę wskazać kolejkę A do głównej kolejki i spowoduje to, że wszystkie bloki w kolejce A będą działać w głównej kolejce (zwykle odbywa się to w przypadku aktualizacji interfejsu użytkownika).
'dispatch_get_current_queue()' jest już przestarzałe. Sposobem na wykrycie głównej kolejki jest 'NSThread.isMainThread()' (Swift) lub [NSThread isMainThread] (Objective-C) – udondan
'NSThread.isMainThread()' nie jest niezawodny, ponieważ w rzadkich przypadkach główne bloki kolejki, a GCD ponownie wykorzystuje główny wątek do wykonania innych kolejek. Zobacz [1] (http://blog.krzyzanowskim.com/2016/06/03/queues-are-not-bound-to-any-specific-thread/), [2] (http: //blog.benjamin -encz.de/post/main-queue-vs-main-thread/). – Jano
@jtbandy należy uważać podczas oznaczania pytań jako duplikatów. To pytanie jest wyraźnie starsze i ma o wiele więcej aktywności niż to, które łączyłeś i być może powinny one być zamknięte w odwrotnym kierunku. –