Как «вырваться» из dispatch_apply ()? - PullRequest
7 голосов
/ 24 июня 2010

Есть ли способ имитировать оператор break в блоке dispatch_apply()?

Например, каждый API-интерфейс Какао, который я видел при работе с перечисляющими блоками, имеет "стоп""параметр:

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

Есть ли что-то похожее для GCD?

Ответы [ 3 ]

14 голосов
/ 24 июня 2010

В интерфейсе API dispatch_*() нет понятия отмены. Причина этого заключается в том, что почти всегда верно, что ваш код поддерживает концепцию, когда останавливаться или нет, и, следовательно, также поддерживает то, что в диспетчере _ * () API-интерфейсы будут избыточными (а с избыточностью возникают ошибки).

Таким образом, если вы хотите «остановить досрочно» или иным образом отменить ожидающие элементы в очереди отправки (независимо от того, как они были поставлены в очередь), вы делаете это, разделяя некоторый бит состояния с блокированными блоками, который позволяет вам отменить .

if (is_canceled()) return;

Или:

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

Обратите внимание, что оба enumerateObjectsUsingBlock: и enumerateObjectsWithOptions:usingBlock: поддерживают отмену, потому что этот API находится в другой роли. Вызов метода перечисления является синхронным , даже если фактическое выполнение блоков перечисления может быть полностью параллельным в зависимости от параметров.

Таким образом, установка *stopFlag=YES заставляет перечисление остановиться. Это, однако, не гарантирует, что оно немедленно прекратится в параллельном случае. Фактически перечисление может выполнить еще несколько уже помещенных в очередь блоков перед остановкой.

(Можно вкратце подумать, что было бы более разумно возвращать BOOL, чтобы указать, следует ли продолжать перечисление. Для этого потребовалось бы, чтобы блок перечисления выполнялся синхронно, даже в параллельном случае, так что возврат значение можно проверить. Это было бы гораздо менее эффективно.)

4 голосов
/ 24 июня 2010

Я не думаю, что dispatch_apply поддерживает это. Лучший способ имитировать это - создать логическую переменную __block и проверить ее в начале блока. Если он установлен, выручите быстро. Вам все равно придется выполнить блок через оставшиеся итерации, но это будет быстрее.

1 голос
/ 24 июня 2010

Вы не можете break a dispatch_apply, поскольку это нелогично.

В -enumerateObjectsUsingBlock: разрыв четко определен, потому что функции выполняются последовательно . Но в dispatch_apply функции выполняются параллельно. Это означает, что при i=3 первом вызове "блока" i=4-й вызов мог быть начат. Если вы break на i=3, должен ли вызов i=4 все еще выполняться?

@ BJ ответ - самый близкий, который вы можете сделать, но всегда будет некоторый «перелив».

...