【问题标题】:dispatch_apply gives incorrect output datadispatch_apply 给出不正确的输出数据
【发布时间】:2015-04-15 15:24:12
【问题描述】:

我有两个数组:array1array2。数组的每个对象也是一个数组(二维数组)。通过这种方式,我将它们多个。那么我如何拥有使用dispatch_apply 的大数组。每次我收到不同的结果时,都包括一个正确的结果。也许有人知道如何解决它?

    dispatch_apply([array2 count], queue, ^(size_t j)
    {
        k = 0;
        for (int l = 0; l < [[array1 objectAtIndex:0] count]; l++) {
            k += [[[array1 objectAtIndex:i] objectAtIndex:l] intValue] *
                   [[[array2 objectAtIndex:j] objectAtIndex:l] intValue];
        }
        kNSNumber = [NSNumber numberWithInt:k];
        [multipliedArrayInto replaceObjectAtIndex:j withObject:kNSNumber];
    });
    [resulArray insertObject:multipliedArrayInto atIndex:i];
}

【问题讨论】:

  • 在你的例子中'i'来自哪里?除此之外,这看起来充满了竞争条件。尝试 for 或 for-each 循环,无需并行化此类任务。
  • 拜托,拜托,请把你的 Objective-C 代码扔掉,用普通的旧 C 来做这件事。这不是 Objective-C 应该做的事情。您的代码将比它应该运行的速度慢大约 1,000 倍。想想 dispatch_apply 是并发的,而 NSMutableArray 不是线程安全的,这会带来什么后果。

标签: objective-c grand-central-dispatch dispatch


【解决方案1】:

我可以建议两件事,我敢打赌其中之一(或两者)是解决您问题的总体解决方案。

首先,我将k 声明为块的本地,因此毫无疑问您是否正在覆盖它。您可能对块内的kNSNumber 有同样的问题。如果您只是使用该 NSNumber 实例猛击multipliedArrayInto 累加器,您也可以删除kNSNumber,并在它的位置使用@(k)(如果只是为了更具可读性)。同样,确保multipliedArrayIntodispatch_apply 之前声明,看起来像一个外部for 循环(i 来自哪里)。最后,确保 resulArray 被实例化,或者在外部 for 循环之前准备好。

其次,queue 是并发队列还是串行队列?如果您使用 dispatch_apply 像并行执行 for/enumeration 一样 - 我认为这很可能,所以您正在有效地处理“大数组” - 那么您实际上可以保证 k 被覆盖。如果您将其更改为串行,它可能会按设计工作。如果您希望它是并行的,则需要将 k 累加器的声明移动到块内,并确保其他变量的声明也有意义。

更新以反映问题更新:

@antonytonies 理想情况下,您对该主题的后续回答应移至问题本身,以便人们可以更轻松地关注该主题。

所以,看来我所描述的正是你的问题。

全局队列都是并发队列,这意味着(假设)所有调度块都在一次执行,k 和其他变量的内容会根据如何被吹走块的执行顺序。

我已获取您的更新(在您添加的“答案”中),并将其修改为可能有效:

// I renamed your method, because nameless parameters pain me. This is cosmetic, and doesn't
// matter for the problem at hand.
- (NSMutableArray *)multiplicationArrays:(NSMutableArray *)array vector:(NSMutableArray *)vector
{
  // IMHO, you want to set resultArray to nil here. Another option is to set it to nil in the
  // else case, below. Properties in Objective-C are initalized to nil,0,false,etc; you can
  // rely on ARC to initialize pointer to objc objects on the stack, too. However, someone
  // reading this code may or may not know that. IMHO, using the explicitly assignement makes it
  // clear that you're going to be returning `nil` or an instance of `NSMutableArray`.
  NSMutableArray *resultArray = nil;

  if ([[array objectAtIndex:0] count] == [vector count]) {

    // Nicely done w/ pre-allocating the result array here, so that there's no question
    // of the indexes matches the results later on.
    resultArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
    for (int i=0; i < [array count]; i++) {
      [resultArray insertObject:[NSNull null] atIndex:i];
    }

    // 'queue' here is a concurrent queue. This means that you are proclaiming to the runtime
    // that the blocks being executed are able to operate correctly w/o interference from each
    // other. This is also thought of in terms of parallel execution: all these blocks may run
    // **at once**. This *also* means, that you must not share storage between them.
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_apply([array count], queue, ^(size_t j) {
      // Moved 'result' inside the block.
      NSInteger result = 0;

      for (int l = 0; l < [[array objectAtIndex:0] count]; l++) {
        // These array reads are **NOT** thread safe. They probably don't cause must trouble in
        // practice, but you may want to reconfigure this.
        result += [[[array objectAtIndex:j] objectAtIndex:l] intValue] * [[vector objectAtIndex:l] intValue];
      }

      // The replace of the object into resultArray is **NOT** thread-safe.
      // This probably hasn't caused you much trouble, since you can guarantee that
      // you aren't writing at the same index. However, I would strongly suggest to
      // change this to be thread-safe.
      [resultArray replaceObjectAtIndex:j withObject:@(result)];
    });

  }
  else {
    NSLog(@"matrix count isn't correspond");
  }

  return resultArray;
}

最后:考虑使用 Apple 的 Accelerate 框架来解决此类问题。它在 OSX 和 iOS 上可用,因此您应该涵盖所有基础。

【讨论】:

  • 我之前向区块声明了k, multipliedArrayInto, resultArray。我的队列是dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 如果我将 2D-array 和 vector 组合起来,这是一回事
  • @antonytonies 我使用您的第二篇文章更新了我的答案,以解释您必须做出的最小更改。正如我所指出的,这仍然不会使您的代码 100% 线程安全。
【解决方案2】:

如果我多个二维数组和向量是一样的

-(NSMutableArray*)multiplicationArraysWithVector:(NSMutableArray *)array :(NSMutableArray *)vector
{
    NSMutableArray* resultArray;

    if ([[array objectAtIndex:0] count] == [vector count])
         {
             resultArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
             for (int i=0; i < [array count]; i++) {
                 [resultArray insertObject:[NSNull null] atIndex:i];
             }

              __block NSInteger result;

             dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
             dispatch_apply([array count], queue, ^(size_t j)
                            {
                                result = 0;


                                for (int l = 0; l < [[array objectAtIndex:0] count]; l++) {
                                    result += [[[array objectAtIndex:j] objectAtIndex:l] intValue] * [[vector objectAtIndex:l]intValue];
                                }

                                [resultArray replaceObjectAtIndex:j withObject:@(result)];
                            });


    }
    else
    {
        NSLog(@"matrix count isn't correspond");
    }

    return  resultArray;

}

在这种情况下,我可以获得正确或错误的数据结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多