如何“打破”超出dispatch_apply()?

发布于 2024-09-06 11:33:32 字数 559 浏览 8 评论 0原文

有没有办法在 dispatch_apply() 块?

例如,我见过的每个处理枚举块的 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 有类似的东西吗?

Is there a way to simulate a break statement in a dispatch_apply() block?

E.g., every Cocoa API I've seen dealing with enumerating blocks has a "stop" parameter:

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

Is there something similar for GCD?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

音栖息无 2024-09-13 11:33:33

根据设计,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 来指示枚举是否应该继续会更合理。这样做会要求枚举块同步执行,即使在并发情况下也是如此,这样可以检查返回值。这会大大降低效率。)

By design, dispatch_*() APIs have no notion of cancellation. The reason for this is because it is almost universally true that your code maintains the concept of when to stop or not and, thus, also supporting that in the dispatch_*() APIs would be redundant (and, with redundancy comes errors).

Thus, if you want to "stop early" or otherwise cancel the pending items in a dispatch queue (regardless of how they were enqueued), you do so by sharing some bit of state with the enqueued blocks that allows you to cancel.

if (is_canceled()) return;

Or:

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

Note that both enumerateObjectsUsingBlock: and enumerateObjectsWithOptions:usingBlock: both support cancellation because that API is in a different role. The call to the enumeration method is synchronous even if the the actual execution of the enumerating blocks may be fully concurrent depending on options.

Thus, setting the *stopFlag=YES tells the enumeration to stop. It does not, however, guarantee that it will stop immediately in the concurrent case. The enumeration may, in fact, execute a few more already enqueued blocks before stopping.

(One might briefly think that it would be more reasonable to return BOOL to indicate whether the enumeration should continue. Doing so would have required that the enumerating block be executed synchronously, even in the concurrent case, so that the return value could be checked. This would have been vastly less efficient.)

跨年 2024-09-13 11:33:33

我认为dispatch_apply不支持这一点。我能想到的模仿它的最好方法是创建一个 __block 布尔变量,并在块的开头检查它。如果确定了,就赶紧退出。您仍然需要在其余的迭代中运行该块,但它会更快。

I don't think dispatch_apply supports this. The best way I can think of to imitate it would be to make a __block boolean variable, and check it at the beginning of the block. If it's set, bail out quickly. You'd still have to run the block through the rest of the iterations, but it would be faster.

只是在用心讲痛 2024-09-13 11:33:33

您无法破坏 dispatch_apply,因为它不合逻辑。

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

@BJ 的答案是最接近的这样做,但总会有一些“溢出”。

You can't break a dispatch_apply since it's illogical.

In -enumerateObjectsUsingBlock: a break is well-defined because the functions are run sequentially. But in dispatch_apply the functions are run in parallel. That means at the i=3rd invocation of the "block", the i=4th call could have been started. If you break at i=3, should the i=4 call still run?

@BJ's answer is the closest you can do, but there will always some "spill-over".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文