Need to clarify the behavior of dispatch_group_wait () when dispatch_group_create () and dispatch_group_enter () are called from different queues

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

The first code shown in the Code That Works section is straight from the tutorial. The Alert view (final completion block) is executed after all 3 downloads have completed.

I tried to play with it and moved the async dispatch to "Code That Doesn't Work" to see what happens if dispatch_group_create () and dispatch_group_enter () are executed on different queues. In this case, dispatch_group_enter () doesn't seem to be registered because dispatch_group_wait () exits immediately and the warning (final completion block) is executed even before all downloads have finished.

Can someone explain what happens in the second case? (This is just for my understanding of how the dispatch group works, and I understand that it is better to place the entire function in a global parallel 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 doesn't work with extra NSLog instructions

Code that doesn't 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);
    }
}) ;
}

      

0


source to share


1 answer


" dispatch_group_enter()

does not seem to be logged" because it has not been called yet by the time it is called dispatch_group_wait()

. Or rather, it was not guaranteed to be called. There is a race condition.

This does not apply to different queues. It's about concurrency and asynchrony.

dispatch_async()

simply means "add a task to the list" with an implicit understanding that something, somewhere, someday will remove tasks from this list and execute them. It returns to the caller immediately after the task has been placed on the list. It doesn't wait for the task to start, let alone finish the job.



So, your loop for

is running very fast, and by the time it exits, it might happen that none of the tasks it has queued up are running. Or, if someone has started it, it is possible that they have not finished logging into the group yet.

Your code can complete its call before dispatch_group_wait()

anything enters the group.

Usually, you want to make sure that all relevant calls are dispatch_group_enter()

completed before the call dispatch_group_wait()

. The easiest way to do this is to make it all work synchronously in the same execution context. That is, don't put calls to dispatch_group_enter()

inside blocks that are dispatched asynchronously.

+3


source







All Articles