6

Mam klasy object-c z niektórymi metodami, które używają kolejki GCD, aby zapewnić równoległy dostęp do zasobu odbywa się serialnie (standardowy sposób to zrobić).Jak zaimplementować mechanizm blokowania z ponownym przypisywaniem w obiekcie-c poprzez GCD?

Niektóre z tych metod wymagają wywoływania innych metod tej samej klasy. Tak więc mechanizm blokujący musi być ponownie wprowadzony. Czy istnieje standardowy sposób, aby to zrobić?

Początkowo miałem każda z tych metod użyć

dispatch_sync(my_queue, ^{ 

    // Critical section 

}); 

zsynchronizować dostępy. Jak wiecie, gdy jedna z tych metod wywoła inną taką metodę, pojawia się zakleszczenie, ponieważ wywołanie dispatch_sync zatrzymuje bieżące wykonywanie, dopóki ten drugi blok nie zostanie wykonany, co nie może być również wykonane, ponieważ wykonanie w kolejce jest zatrzymane. Aby rozwiązać ten problem, użyłem np. Ta metoda:

- (void) executeOnQueueSync:(dispatch_queue_t)queue : (void (^)(void))theBlock { 
    if (dispatch_get_current_queue() == queue) { 
     theBlock(); 
    } else { 
     dispatch_sync(queue, theBlock); 
    } 
} 

I w każdym z moich metod, używam

[self executeOnQueueSync:my_queue : ^{ 

    // Critical section 

}]; 

Nie lubię tego rozwiązania, ponieważ dla każdego bloku z innym typem powrotu, muszę napisać inną metodę. Ponadto problem ten wydaje mi się bardzo powszechny i ​​uważam, że powinno istnieć lepsze, standardowe rozwiązanie tego problemu.

+0

Czy zamiast tego użyłeś opcji '@ synchronized'? –

+1

@MartinR, tak, ale '@ synchronized' jest klasycznym blokadą, nie opartym na GCD/kolejkach, i dlatego, jak rozumiem, nie należy go używać ze względu na prostotę i wydajność kodu. Tytuł tego pytania jest zatem mylący, ponieważ zawiera "blokadę". Chodzi mi o synchronizowanie dostępu w sposób bezpośredni z GDC/kolejkami. Po prostu nie miałem lepszych słów niż "blokowanie ponownego wejścia", ponieważ jest to nazwa rozwiązania problemu, które większość ludzi zna. –

+1

@MartinR Ponadto, '' zsynchronizowane' ma tę zaletę, że prowadzi do prostszego kodu dla reentranta - bez względu na to - (jak nazwać to najlepiej?). Mimo to blokujący mechanizm ma mniejszą wydajność niż mechanizm stojący za kolejkami GCD. –

Odpowiedz

10

Po pierwsze: dispatch_get_current_queue() jest przestarzałe. Kanonicznym podejściem byłoby teraz użycie dispatch_queue_set_specific. Jednym z takich przykładów może wyglądać następująco:

typedef dispatch_queue_t dispatch_recursive_queue_t; 
static const void * const RecursiveKey = (const void*)&RecursiveKey; 

dispatch_recursive_queue_t dispatch_queue_create_recursive_serial(const char * name) 
{ 
    dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL); 
    dispatch_queue_set_specific(queue, RecursiveKey, (__bridge void *)(queue), NULL); 
    return queue; 
} 

void dispatch_sync_recursive(dispatch_recursive_queue_t queue, dispatch_block_t block) 
{ 
    if (dispatch_get_specific(RecursiveKey) == (__bridge void *)(queue)) 
     block(); 
    else 
     dispatch_sync(queue, block); 
} 

Ten wzór jest bardzo użyteczny, ale nie jest to zapewne kuloodporny, ponieważ można utworzyć zagnieżdżone rekurencyjnych kolejki z dispatch_set_target_queue i próbuje enqueue prace nad kolejce zewnętrznej od wewnątrz wewnętrznej jednym zakleszczyłoby się, nawet jeśli już jesteś "w zamku" (w cudzysłowie, ponieważ tylko wygląda jak zamek, to w rzeczywistości coś innego: kolejka - stąd pytanie, prawda?) na zewnętrzną. (Można obejść to przez zawijania wywołań dispatch_set_target_queue i utrzymanie własnego out-of-band kierowania wykres, etc., ale to pozostawiamy jako ćwiczenie dla czytelnika.)

iść na powiedzieć:

Nie podoba mi się to rozwiązanie, ponieważ dla każdego bloku z różnymi typami zwracania , muszę napisać inną metodę.

Ogólną ideą tej "chroniącej stan kolejki szeregowej" jest to, że chronisz prywatny stan; dlaczego miałbyś "przynieść swoją własną kolejkę" do tego? Jeśli chodzi o wiele obiektów współdzielących ochronę państwa, to podaj im nieodłączny sposób znalezienia kolejki (tj. Albo wciśnij ją w czasie inicjacji, albo umieść gdzieś, co jest dostępne dla wszystkich zainteresowanych stron). Nie jest jasne, w jaki sposób przydałoby się tutaj "przynoszenie własnej kolejki".

+2

Dziękuję za wyjaśnienie dotyczące przestarzałej funkcji i tego, czego należy używać, i jak. To samo jest warte wiele. –

+0

@ipmcc, jestem naprawdę pod wrażeniem twoich odpowiedzi i komentarzy do tego i powiązanych tematów. Czy mógłbyś także spojrzeć na to [pytanie] (http://stackoverflow.com/questions/20201078/how-to-implement-a-reentrant-locking-mechanism-trough-dispatch-concurrent-queue) Właśnie napisałem ? –

+0

@ipmcc Czy to podejście różni się efektywnością lub efektywnością w przeciwieństwie do używania kolejki z etykietą i 'dispatch_queue_get_label (DISPATCH_CURRENT_QUEUE_LABEL)? – Orangenhain