7

Czy istnieje sposób symulacji instrukcji break w bloku dispatch_apply()?Jak "wyrwać" się z dispatch_apply()?

przykład każdy Cocoa API Widziałem czynienia z wyliczanie bloków ma „stop” parametr:

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *stop) { 
    if ([obj isNotVeryNice]) { 
     *stop = YES; // No more enumerating! 
    } else { 
     NSLog(@"%@ at %zu", obj, i); 
    } 
}]; 

czy istnieje coś podobnego do GCD?

Odpowiedz

4

Nie sądzę dispatch_apply obsługuje tego. Najlepszym sposobem naśladowania go byłoby utworzenie zmiennej boolean __block i sprawdzenie jej na początku bloku. Jeśli jest ustawiony, wyskocz szybko. Nadal będziesz musiał uruchomić blok przez resztę iteracji, ale będzie szybciej.

1

Nie możesz breakdispatch_apply ponieważ jest to nielogiczne.

W -enumerateObjectsUsingBlock: przerwa jest dobrze zdefiniowana, ponieważ funkcje są kolejno . Ale w dispatch_apply funkcje są uruchamiane równolegle. Oznacza to, że na i=3 rd wywołanie "bloku", można było rozpocząć połączenie od i=4. Jeśli masz break pod numerem i=3, czy wywołanie i=4 nadal działa?

@BJ „s odpowiedź jest najbliżej można zrobić, ale nie zawsze będzie jakiś«spill-over».

+0

Cóż, to nie do końca prawda; podczas gdy prawdą jest, że enumerateObjectsUsingBlock: jest sekwencyjny, istnieje również enumerateObjectsWithOptions: usingBlock :. Ten parametr "options" może być użyty do tego, aby wyliczanie odbywało się jednocześnie. Nie jestem pewien, jak to robią wewnętrznie, ale domyślam się, że zrobiono to za pomocą grupy dispatch_group, która umożliwiałaby bardziej bezpośrednią kontrolę. –

+0

Ale chodzi o to, że enumerateObjectsWithOptions: usingBlock: nadal ma parametr * stop. –

+1

Logiczne byłoby wspieranie zatrzymywania w dispatch_apply(), ale nie miało to sensu w ramach celów projektowych. Stwierdzenie, że flaga stop na elemencie 'enumerateObjectsUsingBlock:' istnieje z powodu domniemanego wykonywania sekwencyjnego, jest niepoprawna; te dwie są całkowicie ortogonalne. – bbum

14

Zgodnie z projektem, dispatch_*() API nie mają pojęcia odwołania. Powodem tego jest fakt, że prawie zawsze jest prawdą, że twój kod zachowuje koncepcję, kiedy się zatrzymać, a co nie, a także, że w wysyłce _ *() API byłyby zbędne (a przy nadmiarowości pojawiają się błędy).

Tak więc, jeśli chcesz "zatrzymać wcześnie" lub w inny sposób anulować oczekujące elementy w kolejce wysyłkowej (niezależnie od tego, w jaki sposób zostały zakolejkowane), możesz to zrobić, dzieląc trochę stanu z kolejnymi blokami, które pozwalają ci Anuluj.

if (is_canceled()) return; 

Lub:

__block BOOL keepGoing = YES; 
dispatch_*(someQueue, ^{ 
    if (!keepGoing) return; 
    if (weAreDoneNow) keepGoing = NO; 
} 

Należy zauważyć, że zarówno enumerateObjectsUsingBlock: i enumerateObjectsWithOptions:usingBlock: zarówno anulowanie wsparcie ponieważ API znajduje się w innej roli. Wywołanie metody wyliczania to synchroniczna, nawet jeśli faktyczne wykonanie bloków wyliczających może być w pełni współbieżne w zależności od opcji.

Zatem ustawienie *stopFlag=YES mówi wyliczenie przestać. Nie gwarantuje to jednak, że zatrzyma się natychmiast w równoległym przypadku. Wyliczenie może faktycznie wykonać kilka bloków, które już są zakryte przed zatrzymaniem.

(Można krótko pomyśleć, że bardziej sensownym byłoby zwracanie numeru BOOL w celu wskazania, czy wyliczanie powinno być kontynuowane, ponieważ wymagałoby to, aby blok wyliczający był wykonywany synchronicznie, nawet w przypadku równoczesnym, aby wartość zwracana była może być sprawdzone, byłoby to znacznie mniej wydajne.)