Как использовать GCD dispatch_barrier_async в iOS (кажется, выполняется до и после других блоков)

Я пытаюсь синхронизировать следующий код в iOS5:

  • у объекта есть метод, который делает HTTP-запрос, из которого он получает некоторые данные, включая URL-адрес изображения
  • после поступления данных, текстовые данные используются для заполнения Модель CoreData​​li >
  • в то же время второй поток отправляется async для загрузки изображение; этот поток будет подавать сигнал через KVO в viewController, когда изображение уже кэшировано и доступно в модели CoreData.
  • так как загрузка изображения займет некоторое время, мы сразу возвращаемся объект CoreData, который имеет все атрибуты, но для изображения вызывающий.
  • Кроме того, когда загружается второй поток, модель 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];
             });

Итак, я действительно не отправляю блок загрузки изображения перед барьером, или барьер будет ждать, пока блок загрузки изображения не будет выполнен (что было моим намерением).

Что я делаю неправильно? как я могу убедиться, что блок загрузки изображения находится в очереди перед барьерным блоком?

Ответы

Ответ 1

На первый взгляд проблема может заключаться в том, что вы отправляете барьерный блок в глобальной параллельной очереди. Вы можете использовать блоки блокировки только в своей собственной параллельной очереди. В документах GCD на dispatch_barrier_async, если вы отправляете блок в глобальную очередь, он будет вести себя как обычный вызов dispatch_async.

Майк Эш имеет хорошее сообщение в блоге по блокам барьеров GCD: http://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

Удачи.

Т

Ответ 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 очень маленькие, и вы можете создавать их без проблем. Вам просто нужно освободить их, когда вы закончите с ними.

Ответ 3

Для того, что вы пытаетесь решить, dispatch_barrier_async может быть не лучшим решением. Посмотрите раздел Миграция из нитей" руководства по программированию Concurrency. Просто используя dispatch_sync в вашей собственной последовательной очереди, вы можете решить вашу проблему синхронизации. Кроме того, вы можете использовать NSOperation и NSOperationQueue. В отличие от GCD, NSOperation позволяет вам легко управлять зависимостями (вы можете делать это с помощью GCD, но это может стать уродливым).