GCD - 具有同步任务的异步队列

时间:2012-10-01 20:22:23

标签: objective-c ios multithreading ipad grand-central-dispatch

我正在尝试从API中下载数据,然后再将其显示给用户。

这就是我在做的事情:

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{

    __block NSMutableArray *newImages;

    dispatch_sync(concurrentQueue, ^{
        newImages = [NSMutableArray array];
        // images retrieved using `NSURLConnection sendSynchronousRequest`
    });

    dispatch_sync(dispatch_get_main_queue(), ^{
        // display images to the user
    });
});

我的问题是,由于newImages是用__block声明的,在主队列执行第二个任务时,我是否始终保证拥有最新的newImages数据?如果没有,那么将该数组的内容传递到第二个块并确保其最新内容的最安全的方法是什么?

我认为我的数据在某个地方搞砸了,所以我问这个原因。

如果这看起来很好,那么我会发布我的完整代码以获取有关其他错误的帮助。

4 个答案:

答案 0 :(得分:2)

解决方案草图:

q = /* custom serial queue */
urls = /* urls array */;
NSMutableArray *images = [NSMutableArray new];
for (NSURL *url in URLs) {
   NSURLRequest *req = [self imageRequestForURL:url];
   dispatch_async(q, ^{
     UIImage *image = [self imageFromRequest:req];
     [images addObject:newImage];
   }
}
dispatch_async(q, ^{
   dispatch_async(dispatch_get_main_queue(), ^{
     [self applyUpdatesForURLs:urls withImages:images];
   });
}

队列是标准的工作队列。

由于在图像下载块之后在串行队列中排队,所以applyUpdatesForURLs:withImages:块保证在所有图像下载后运行。

images没有同步问题,因为使用它的所有代码都是串行运行而不是并发运行。

UI更新最终发生在主线程上。

答案 1 :(得分:1)

您发布的方法看起来过于复杂。为什么不使用NSURLConnection方法sendAsynchronousRequest:queue:completionHandler:。您可以将mainQueue指定为队列参数来更新UI。

    NSURL *url = [NSURL URLWithString:@"your server address"];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *theData, NSError *error){
        if (error == nil) {
            //do what you want with theData and update the UI
        }
    }];

答案 2 :(得分:1)

更新可变数组的并发队列中不能有多个块 - 由于可变容器不是线程安全的,因此它无法正常工作。当图像可用时,在主队列上排队一个块,然后将其添加到阵列中。

答案 3 :(得分:1)

首先,您不能在并发队列上dispatch_sync()。 (嗯,你可以,但它与dispatch_async()完全一样。)dispatch_sync()只在串行队列中产生任何概念上的意义,你要说的是,“我想等到所有块在我结束之前排队,然后执行此块,然后将控制权返回给调用线程。“

其次,rdelmar's answer是正确的 - 你过于复杂了。即使您不想在NSURLConnection上使用批处理完成处理程序,您当然也不需要在并发队列上嵌套两个块调度 - 只有一个块执行批量下载(在并发上运行异步) queue)有一个嵌套块,在完成后在主队列上进行UI更新就好了!