您可能还记得,我正在尝试使用GCD来加速我的一些代码,即碰撞检测和分辨率引擎。但是,我显然做错了,因为我的所有GCD代码都比我的序列代码慢得多且不那么一致(慢了1.4倍到10倍之间)。请允许我举个例子:我以冒泡排序方式迭代数组,以确定该数组中对象之间可能发生的所有冲突:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < count; i++)
{
for (int j = i + 1; j < count; j++)
{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
}
}
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
非常简单,考虑到问题的限制,它似乎表现良好。但是,我想利用以下事实:在代码部分中不修改每个对象的状态,并使用GCD来并行化这项工作。要做到这一点,我正在尝试这样的事情:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
NSBlockOperation* blockOperation = nil;
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < count; i++)
{
for (int j = i + 1; j < count; j++)
{
void (^workBlock) (void) = ^()
{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
};
if (!blockOperation)
{
blockOperation = [NSBlockOperation blockOperationWithBlock:b];
}
else
{
[blockOperation addExecutionBlock:workBlock];
}
}
}
[opQueue addOperation:blockOperation];
[opQueue autorelease];
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
任何人都可以帮助我走上正轨,并提供一个良好的GCD教程的链接吗?我查看了几个GCD教程并搜索了所有文档,我仍然觉得我对这个主题的掌握充其量是微不足道的。谢谢!
答案 0 :(得分:30)
您是否有理由不使用GCD C API和dispatch_*
系列函数?您无法控制NSOperationQueue
的GCD方面(就像您要将块提交到哪个队列一样)。此外,我无法判断您是否使用iOS,但NSOperationQueue
不在iOS上使用GCD。这可能是它催生这么多线程的原因。无论哪种方式,如果直接使用GCD API,您的代码将更短更简单:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
double time = CFAbsoluteTimeGetCurrent();
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < count; i++)
{
dispatch_group_async(group, queue, ^{
for (int j = i + 1; j < count; j++)
{
dispatch_group_async(group, queue, ^{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
});
}
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
您可以使用dispatch_group
将所有执行组合在一起,并等待所有执行完成dispatch_group_wait
。如果您不想知道块何时完成,则可以忽略组部分并使用dispatch_async
。 dispatch_get_global_queue
函数将获得3个并发队列中的一个(低,默认或高优先级)供您提交块。您不必担心限制线程数或类似的东西。 GCD调度程序应该为您完成所有这些。只需确保提交到并发队列,该队列可以是3个全局队列之一,也可以是通过将DISPATCH_QUEUE_CONCURRENT
传递给dispatch_queue_create
创建的队列(这可以从OS X 10.7和iOS 5.0)。
如果您在每个块中执行某些文件I / O或对其他资源征税,则可能需要在GCD中进行控制并限制一次提交到队列的块数。这与限制NSOperationQueue
中的并发操作计数具有相同的效果。您可以使用GCD信号量执行此操作:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
double time = CFAbsoluteTimeGetCurrent();
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < count; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
for (int j = i + 1; j < count; j++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
dispatch_semaphore_signal(semaphore);
});
}
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
dispatch_release(semaphore);
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
一旦掌握了它,GCD使用起来非常简单。我现在在我的代码中使用它。
任何人都可以帮助我走上正轨,并提供一个良好的GCD教程的链接吗?
运行,不要走到Mike Ash's blog。他在GCD上的系列节目是我见过的最清晰,最简洁的,只需要大约30分钟的时间来阅读整篇文章。 Apple的WWDC视频来自2010年的GCD和块也非常好。
答案 1 :(得分:4)
在您的代码中,您将延迟每个对象需要完成的工作,直到嵌套for
循环结束。也就是说,当循环结束时,你将有一个操作,有很多块用于一堆对象,因此你不会正确地利用GCD。
我建议您为每个对象创建一个NSBlockOperation
,并在每个NSOperationQueue
次迭代结束时将其添加到for (int j = i + 1; j < count; j++)
。
这样,一旦迭代结束,系统就会开始处理你需要为每个对象做的工作。
还要记住,队列不应该比可用的处理器大得多,否则在线程切换过程中会有一些开销会降低速度。