如何“打破”dispatch_apply()的循环?

7
有没有办法在 dispatch_apply() 块中模拟一个 break 语句?
例如,我见过的所有处理块枚举的 Cocoa 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

按照设计,dispatch_*() 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

我认为dispatch_apply不支持这个功能。我能想到的最好的模拟方法是使用__block布尔变量,在块的开头检查它。如果设置了它,就快速退出。你仍然需要运行块通过其余的迭代,但速度会更快。


1

dispatch_apply 中,使用 break 是不合逻辑的。

-enumerateObjectsUsingBlock: 中使用 break 是有明确定义的,因为函数是按顺序运行的。但是在 dispatch_apply 中,函数是并行运行的。这意味着在“块”的第三次调用时,“i=4”的调用可能已经开始了。如果在 i=3 处使用 break,那么 i=4 的调用是否仍然会运行?

@BJ 的答案是最接近实际情况的,但仍然会有一些“溢出”。


那并不完全正确;虽然enumerateObjectsUsingBlock:是顺序的,但也有enumerateObjectsWithOptions:usingBlock:。该“options”参数可用于指示枚举应同时发生。我不确定他们在内部如何实现,但我猜测它是使用dispatch_group完成的,这将允许更直接的控制。 - BJ Homer
但是要点在于enumerateObjectsWithOptions:usingBlock:仍然有*stop参数。 - BJ Homer
1
支持在dispatch_apply()中停止是合乎逻辑的,但这在设计目标上没有意义。声称enumerateObjectsUsingBlock:上的停止标志存在是因为暗示了顺序执行是错误的;这两者完全正交。 - bbum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接