【问题标题】:How should I use GCD dispatch_barrier_async in iOS (seems to execute before and not after other blocks)我应该如何在 iOS 中使用 GCD dispatch_barrier_async (似乎在其他块之前而不是之后执行)
【发布时间】:2012-06-04 05:27:15
【问题描述】:

我正在尝试在 iOS5 中同步以下代码:

  1. 一个对象有一个方法可以发出一个HTTP请求 获取一些数据,包括图片的 URL
  2. 一旦数据到达,文本数据将用于填充 CoreData 模型
  3. 同时,调度第二个线程异步下载 图片;该线程将通过 KVO 向 viewController 发送信号 图像已缓存并在 CoreData 模型中可用。
  4. 由于图片下载需要一段时间,我们立即返回 CoreData 对象,它具有除图像之外的所有属性 来电者。
  5. 另外,当第二个线程完成下载时,CoreData 模型 可以保存。

这是(简化的)代码:

- (void)insideSomeMethod
{
    [SomeHTTPRequest withCompletionHandler:
     ^(id retrievedData) 
     {
         if(!retrievedData)
         {
             handler(nil);
         }

         // Populate CoreData model with retrieved Data...

         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
             NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
             aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
         });

         handler(aCoreDataNSManagedObject);
         [self shouldCommitChangesToModel];
     }];
}

- (void)shouldCommitChangesToModel
{
    dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSError *error = nil;
        if(![managedObjectContext save:&error]) 
        {
            //  Handle error
        }  
    });
}

但是,基于屏障的保存块总是在图像加载块之前执行。也就是说,

dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSError *error = nil;
            if(![managedObjectContext save:&error]) 
            {
                //  Handle error
            }  
        });

在之前执行:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                 NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
                 aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
             });

所以显然我并没有真正在屏障之前调度图像加载块,否则屏障会等到图像加载块完成后再执行(这是我的意图)。

我做错了什么?如何确保图像加载块在屏障块之前入队?

【问题讨论】:

    标签: ios cocoa-touch concurrency objective-c-blocks grand-central-dispatch


    【解决方案1】:

    乍一看,问题可能是您在全局并发队列上分派屏障块。您只能在自己的自定义并发队列上使用屏障块。根据 dispatch_barrier_async 上的 GCD 文档,如果您将一个块分派到全局队列,它的行为将类似于正常的 dispatch_async 调用。

    Mike Ash 有一篇关于 GCD 屏障块的好博文:http://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

    祝你好运

    T

    【讨论】:

    • 我自己也想过好几次,但是 Apple 文档不是说在 iOS 中我们不能真正创建自己的队列吗?因此,如果我“创建”一个,我实际上只会获得一个全局别名的别名?
    • 我相信从 ios5 开始你可以创建你的调度队列了。
    • 是的,我刚试过,现在可以了。我会发誓我读到我们无法创建自己的,或者创建它们只会返回一个全局变量,这就是为什么我在上面的代码中明确地选择了一个全局变量。
    【解决方案2】:

    您需要创建自己的队列,而不是按照 ADC 文档分派到全局队列

    您指定的队列应该是您创建的并发队列 自己使用 dispatch_queue_create 函数。如果你排队 传递给此函数的是串行队列或全局队列之一 并发队列,此函数的行为类似于 dispatch_async 功能。

    来自https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_barrier_async

    您可以创建大量自己的 GCD 队列。 gcd 队列非常小,您可以毫无问题地创建大量队列。你只需要在完成它们后释放它们。

    【讨论】:

    • 请注意。如果您的部署目标是 iOS6+ 并且您使用的是 ARC,则无需释放队列(您不能)。 ARC 会为您做到这一点!
    【解决方案3】:

    对于您似乎想要解决的问题,dispatch_barrier_async 可能不是最佳解决方案。 查看并发编程指南的Migrating Away From Threads 部分。只需在您自己的串行队列上使用dispatch_sync 就可以解决您的同步问题。 或者,您可以使用 NSOperation 和 NSOperationQueue。与 GCD 不同,NSOperation 允许您轻松管理依赖项(您可以使用 GCD 来做到这一点,但它会很快变得丑陋)。

    【讨论】:

      【解决方案4】:

      我参加聚会有点晚了,但也许下次你可以尝试使用dispatch_groups 来发挥你的优势。 http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-01
        • 2021-10-02
        • 1970-01-01
        • 1970-01-01
        • 2020-08-11
        • 2020-02-25
        相关资源
        最近更新 更多