9

您可能还记得,我正在尝试使用 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 教程并浏览了所有文档,但我仍然觉得我对这个主题的掌握充其量是微不足道的。谢谢!

4

2 回答 2

30

您是否有理由不使用 GCD C API 和dispatch_*函数系列?您对 GCD 方面没有太多控制权NSOperationQueue(例如要将块提交到哪个队列)。另外,我不知道您是否使用 iOS,但在 iOSNSOperationQueue上不使用 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;
}

您可以使用 adispatch_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 的博客上。他关于 GCD 的系列是我见过的最清晰、最简洁的系列,阅读全文只需大约 30 分钟。2010 年苹果在 GCD 和区块上的 WWDC 视频也很不错。

于 2011-02-22T20:29:58.690 回答
4

在您的代码中,您将需要为每个对象执行的工作延迟到嵌套for循环结束。也就是说,当循环结束时,您将进行一个操作,其中包含大量对象的大量块,因此您将无法正确利用 GCD。

我建议您NSBlockOperation为每个对象创建一个,并在每次迭代NSOperationQueue结束时将其添加到。for (int j = i + 1; j < count; j++)

这样,系统将在迭代结束后立即开始处理您需要为每个对象执行的工作。

还要记住,队列不应该比可用处理器大很多,否则线程切换过程会有一些开销,这会影响速度。

于 2011-02-20T21:16:05.927 回答