Need clarification on dispatch_group_wait() behavior when dispatch_group_create() and dispatch_group_enter() are called from different queues

风格不统一 提交于 2020-01-17 01:31:06

问题


I am looking at the Ray Wenderlich tutorial on using dispatch queues to get notified when a group of tasks complete. http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2

The first code shown under "Code that works" is straight from the tutorial. The Alert view(final completion block) get executed after all 3 downloads complete.

I tried to play around with it and moved the dispatch async down in the "Code that does not work" to see what will happen if dispatch_group_create() and dispatch_group_enter() happen on different queues. In this case, the dispatch_group_enter() does not seem to register because the dispatch_group_wait() immediately completes and alert view(final completion block) is executed even before all the downloads have completed.

Can someone explain whats happening in the second case? (This is just for my understanding of how dispatch group works and I realize thats its better to put the entire function in the global concurrent queue to avoid blocking the main thread).

Code that works

 - (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 Code that does not work with extra NSLog statements

Code that does not work

 - (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:


The "dispatch_group_enter() does not seem to register" because it hasn't actually been called yet by the time that dispatch_group_wait() is called. Or, rather, it's not guaranteed to have been called. There's a race condition.

This isn't specifically about different queues. It's about concurrency and asynchronicity.

dispatch_async() just means "add a task to a list" with an implicit understanding that something, somewhere, somewhen will take tasks off of that list and execute them. It returns to its caller immediately after the task has been put on the list. It does not wait for the task to start running, let alone complete running.

So, your for loop runs very quickly and by the time it exits, it may be that none of the tasks that it has queued have started. Or, if any have started, it may be that they haven't finished entering the group.

Your code may complete its call to dispatch_group_wait() before anything has entered the group.

Usually, you want to be sure that all relevant calls to dispatch_group_enter() have completed before the call to dispatch_group_wait() is made. The easiest way to do that is to have them all happen synchronously in one execution context. That is, don't put calls to dispatch_group_enter() inside blocks that are dispatched asynchronously.



来源:https://stackoverflow.com/questions/35545385/need-clarification-on-dispatch-group-wait-behavior-when-dispatch-group-create

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!