我时不时地注意到我正在使用块来迭代集合而不写入任何共享数据或导致任何副作用。我考虑添加一个NSEnumerationConcurrent选项,然后决定反对它,因为我真的不明白它什么时候值得使用。
所以我有一个特定的问题,一个更普遍的问题。
第一个问题:这是一个使用块来同时做一些微不足道的可能有点人为的例子:
CGFloat GetAverageHeight(NSArray* people)
{
NSUInteger count = [people count];
CGFloat* heights = malloc(sizeof(CGFloat) * count);
[people enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock:
^(id person, NSUInteger idx, BOOL* stop)
{
heights[idx] = [person height];
}];
CGFloat total= 0.0;
for (size_t i = 0 ; i < count ; i++) total += heights[i];
free(heights);
return total / count;
}
忽略非并发枚举可能只是直接求和高度这一事实,而不需要调用malloc或函数的后半部分,在这里使用NSEnumerationConcurrent有什么意义吗?使用GCD(或任何NSEnumerationConcurrent在后台执行的操作)的开销是否会抵消同时获取平凡属性的收益?在使用NSEnumerationConcurrent之前,块的工作需要多少微不足道的工作?
第二个问题:更一般地说,当我看到机会这样做时,我应该考虑将并发性作为我应该使用的东西(理由:可能这些API的重点在于它们使并发性不是特殊情况而是更多部分程序的一般构成),或者只是我应该只在我发现特定性能问题并且认为并发就是答案时才会使用的优化(理由:并发代码中的错误是追踪的噩梦)?
答案 0 :(得分:11)
通常,当要执行的操作相对“繁重”时,您只使用并发。即使这样,如果并行性对于手头的任务来说是错误的粒度,那么使用enumerateObjectsWithOptions:
提供的原始并发很容易成为问题。
GCD在排队和处理内容时非常有效,但该代码很可能最终会调用malloc()来复制块(取决于块是否具有唯一的捕获状态)。
你的第二个问题的答案填补了许多书籍,最无用的。
采用非并发代码并使其并发通常是一个非常困难的问题,充满了噩梦般的错误。然而,预先设计并发可能非常耗时。更糟糕的是,在未实际使用它的情况下实现未来的并发性只会导致在打开它时出现噩梦般的调试体验。
一个关键点;在考虑并发时,专注于使对象的整个子图线程隔离,除了跨越线程/队列的非常明确定义的边界API。核心数据就是一个很好的例子。