当从不同队列

时间:2016-02-22 03:33:27

标签: objective-c multithreading cocoa-touch grand-central-dispatch dispatch-async

我正在查看有关使用调度队列在一组任务完成时收到通知的Ray Wenderlich教程。 http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2

“可行的代码”下显示的第一个代码直接来自教程。完成所有3次下载后,将执行警报视图(最终完成块)。

我尝试使用它并在“无法运行的代码”中将调度异步调低,以查看如果dispatch_group_create()和dispatch_group_enter()在不同的队列上发生会发生什么。在这种情况下,dispatch_group_enter()似乎没有注册,因为dispatch_group_wait()立即完成,并且即使在所有下载完成之前也会执行警报视图(最终完成块)。

有人可以解释第二种情况中发生的事情吗? (这只是为了我对dispatch组如何工作的理解,我意识到将整个函数放在全局并发队列中以避免阻塞主线程更好。)

有效的代码

 - (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{

    __block NSError *error;
    dispatch_group_t downloadGroup = dispatch_group_create();

    for (NSInteger i = 0; i < 3; i++)
    {
        NSURL *url;
        switch (i) {
            case 0:
                url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
                break;
            case 1:
                url = [NSURL URLWithString:kSuccessKidURLString];
                break;
            case 2:
                url = [NSURL URLWithString:kLotsOfFacesURLString];
                break;
            default:
                break;
        }


            dispatch_group_enter(downloadGroup);
            __block Photo *photo = [[Photo alloc] initwithURL:url
                                  withCompletionBlock:^(UIImage *image, NSError *_error) {
                                      if (_error) {
                                          error = _error;
                                      }
                                      NSLog(@"Finished completion block for photo alloc for URL %@ and photo is %@",url,photo) ;
                                      dispatch_group_leave(downloadGroup);
                                  }];

            [[PhotoManager sharedManager] addPhoto:photo];
            NSLog(@"Finished adding photo to shared manager for URL %@ and photo is %@",url,photo) ;
    }

    dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5
    dispatch_async(dispatch_get_main_queue(), ^{
        if (completionBlock) {
            NSLog(@"Executing completion block after download group complete") ;
            completionBlock(error);
        }
    }) ;
  }) ;
}

EDITED代码无法使用额外的NSLog语句

不起作用的代码

 - (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
 {

__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();

for (NSInteger i = 0; i < 3; i++)
{
    NSURL *url;
    switch (i) {
        case 0:
            url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
            break;
        case 1:
            url = [NSURL URLWithString:kSuccessKidURLString];
            break;
        case 2:
            url = [NSURL URLWithString:kLotsOfFacesURLString];
            break;
        default:
            break;
    }

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
        dispatch_group_enter(downloadGroup);
        NSLog(@"Enetered group for URL %@",url) ;
        __block Photo *photo = [[Photo alloc] initwithURL:url
                                      withCompletionBlock:^(UIImage *image, NSError *_error) {
                                          if (_error) {
                                              error = _error;
                                          }
                                          NSLog(@"Finished completion block for photo alloc for URL %@ and photo is %@",url,photo) ;
                                          dispatch_group_leave(downloadGroup);
                                      }];

        [[PhotoManager sharedManager] addPhoto:photo];
        NSLog(@"Finished adding photo to shared manager for URL %@ and photo is %@",url,photo) ;
    }) ;
}

NSLog(@"Executing wait statement") ;
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5
dispatch_async(dispatch_get_main_queue(), ^{
    if (completionBlock) {
        NSLog(@"Executing completion block after download group complete") ;
        completionBlock(error);
    }
}) ;
}

1 个答案:

答案 0 :(得分:3)

dispatch_group_enter()似乎没有注册”,因为到dispatch_group_wait()被调用时它实际上还没有被调用过。或者,相反,它不能保证被调用。有竞争条件。

这不是关于不同队列的具体内容。它是关于并发性和异步性的。

dispatch_async()只是意味着“将任务添加到列表中”,其中隐含的理解是某些东西,某些地方,某些人将从该列表中取出任务并执行它们。在将任务放入列表后,它立即返回其调用者。它不会等待任务开始运行,更不用说完全运行了。

因此,您的for循环运行得非常快,当它退出时,可能是已排队的任务的 none 已经启动。或者,如果有任何已经开始,可能是他们还没有完成进入该组。

您的代码可能会在任何内容进入群组之前完成对dispatch_group_wait()的调用。

通常,您需要确保在调用dispatch_group_enter()之前已完成对dispatch_group_wait()的所有相关调用。最简单的方法是让它们在一个执行上下文中同步发生。也就是说,不要在异步调度的块内调用dispatch_group_enter()