2013-03-25 2 views
4

У меня есть метод, который порой может быть вызван во всем моем коде. Ниже приведен очень простой пример, поскольку код обрабатывает изображения и файлы с фотогалереи iphone и маркирует их уже обработанными, когда это делается с помощью метода.Последовательная очередь GCD, похоже, не выполняется серийно

@property (nonatomic, assign) dispatch_queue_t serialQueue; 

.... 

-(void)processImages 
{ 
    dispatch_async(self.serialQueue, ^{ 
     //block to process images 
     NSLog(@"In processImages"); 

     .... 

     NSLog(@"Done with processImages"); 
    }); 
} 

Я думаю, что каждый раз, когда этот метод называется я хотел бы получить ниже выходной ... «В processImages» «Сделано с processImages» «В processImages» «Совершено с processImages» и т.д. ...

, но я всегда получаю

"в processImages" "в processImages" "Сделано с processImages" «Сделано с процессом Изображения " etc ...

Я думал, что последовательная очередь будет ждать, пока первый блок не будет выполнен, а затем запустите. Для меня, похоже, он запускает метод, затем он снова вызывается и запускается до того, как первый вызов даже закончится, создавая дубликаты изображений, которые обычно не обрабатываются из-за того, что если он действительно будет выполняться серийно, этот метод будет знать, что они были уже обработаны. Возможно, мое понимание серийных очередей не является конкретным. Любой вход? Спасибо.

EDIT: БОЛЬШЕ Контекст ниже, это то, что происходит в блоке ... Может ли это вызвать проблему ???

@property (nonatomic, assign) dispatch_queue_t serialQueue; 

.... 

-(void)processImages 
{ 
    dispatch_async(self.serialQueue, ^{ 
     //library is a reference to ALAssetsLibrary object 

     [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) 
     { 
      [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop) 
      { 
      .... 
      //Process the photos here 
      }]; 
     failureBlock:^(NSError *error) { NSLog(@"Error loading images from library"); 
     }]; 

    }); 
} 

-(id)init 
{ 
    self = [super init]; 
    if(self) 
    { 
     _serialQueue = dispatch_queue_create("com.image.queue",NULL); 
    } 
    return self; 
} 

этот объект создается только один раз, и, насколько я могу сказать, никогда не может быть создан снова основаны от моего кода ... Я буду запускать тесты, чтобы убедиться, что, хотя.

UPDATE 2: ЧТО ДУМАЮ ПРОИСХОДИТ, прокомментируйте, пожалуйста, если вы согласны/не согласны ....

Очевидно, что моя главная проблема в том, что он, кажется, этот блок кода выполняется одновременно, создавая повторяющиеся записи (дважды импортировать одну и ту же фотографию), когда это обычно не делалось, если бы оно запускалось серийно. Когда фото обрабатывается, к нему применяется «грязный» бит, который при следующем вызове метода пропускает это изображение, но этого не происходит, а некоторые изображения обрабатываются дважды. Может ли это быть связано с тем, что я перечисляю объекты во второй очереди, используя enumerategroupswithtypes: внутри этого serialQueue?

  1. processImages вызова
  2. enumerateObjects
  3. немедленно вернуться из enumerateObjects, так как это асинхронная себя
  4. отбой в processImages

processImages не реально сделано, хотя из-за того, что enumerategroups является вероятно, все еще работает, но очередь может быть выполнена, так как она доходит до конца блока до того, как enumerategroups завершили работу. Это кажется мне возможностью?

+0

Интересно, если вы случайно вызова 'dispatch_queue_create' несколько раз. Возможно, там есть инструкция 'NSLog'. Ваши ожидания верны (что он должен быть строго последовательным), но есть что-то еще простое. – Rob

+0

без лишнего контекста, трудно ответить, но ваше предположение верно. Интересно, используете ли вы несколько объектов, у которых есть свои очереди. Убедитесь, что ваша очередь - это общий доступ ко всем. Попробуйте распечатать текущий поток и значение serialQueue в NSLogs. –

+0

Можете ли вы установить точку останова на вызов 'dispatch_async' и проверить, что' serialQueue' является тем же самым объектом каждый раз, когда вызывается 'processImages'. Кажется, что ваша очередь воссоздана. –

ответ

0

Если вопрос: «Может ли последовательная очередь выполнять задачи асинхронно?» тогда ответ - нет. Если вы считаете, что это возможно, вы должны убедиться, что все задачи действительно выполняются в одной очереди.Вы можете добавить следующую строку в блоке, и сравните результат:

dispatch_async(self.serialQueue, ^{ 
    NSLog(@"current queue:%p current thread:%@",dispatch_get_current_queue(),[NSThread currentThread]); 

Убедитесь, что вы пишете NSLog в блоке, который выполняет на очереди, а не в enumerateGroupsWithTypes: usingBlock: failureBlock: Также вы можете попробовать чтобы создать свою очередь, как этот

dispatch_queue_create("label", DISPATCH_QUEUE_SERIAL); 

, но я не думаю, что изменится что-нибудь

EDIT: Кстати, метод

enumerateGroupsWithTypes:usingBlock:failureBlock: 

является асинхронным, почему вы вызываете его в другой очереди?

UPDATE 2: я могу предложить что-то вроде этого:

dispatch_async(queue, ^{ 
    NSLog(@"queue"); 

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER, *pmutex = &mutex; 
    pthread_mutex_lock(pmutex); 

    ALAssetsLibraryGroupsEnumerationResultsBlock listGroupBlock = ^(ALAssetsGroup *group, BOOL *stop) { 
     NSLog(@"block"); 
     if (group) { 
      [groups addObject:group]; 
     } else { 

      [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; 
      dispatch_async(dispatch_get_current_queue(), ^{ 
       pthread_mutex_unlock(pmutex); 
      }); 
     } 
     NSLog(@"block end"); 
    }; 

    [assetsLibrary enumerateGroupsWithTypes:groupTypes usingBlock:listGroupBlock failureBlock:failureBlock]; 
    pthread_mutex_lock(pmutex); 
    pthread_mutex_unlock(pmutex); 
    pthread_mutex_destroy(pmutex); 
    NSLog(@"queue end"); 
}); 
+0

, так что вы говорите, что асинхронная очередь не может запускаться серийно, как я сделал выше? – inks2002

+1

Последовательная очередь всегда будет выполняться последовательно. Ваша проблема в другом месте. –

+0

спасибо, пожалуйста, ознакомьтесь с моим обновлением 2 на основании вашего заявления EDIT. – inks2002

1

Серийные Очереди АБСОЛЮТНО будет выполнять последовательно. Тем не менее, они не гарантируют выполнение одной и той же темы.

Предполагая, что вы используете одну и ту же последовательную очередь, проблемы состоят в том, что NSLog НЕ гарантированно выводит результаты в правильном порядке при вызове одновременно из разных потоков.

вот пример:

  1. SQ работает на нити X, передает "В processImages"
  2. журнала печатает "В прок"
  3. SQ на нити X, посылы "Done с processImages"
  4. SQ работает на нити Y, передает "В processImages"
  5. журнал печатает "essImages \ п"

После 5. NSLog не обязательно знает, что печатать, 3. или 4.

Если вам абсолютно необходимо ведение журнала с учетом времени, вам нужна выделенная очередь для ведения журнала. На практике, у меня не было никаких проблем с только с помощью основной очереди:

dispatch_async(dispatch_get_main_queue(), ^{ 
    NSLog(@"whatever"); 
}); 

Если все вызовы NSLog являются на одной и той же очереди, вы не должны иметь эту проблему.

0

enumerateGroupsWithTypes:usingBlock:failureBlock: выполняет свою работу асинхронно в другом потоке и вызывает блоки, прошедшие в том случае, когда это делается (по основному потоку, который я думаю). Рассматривая это с другой точки зрения, если он завершил все синхронно к моменту завершения вызова метода, он мог бы просто вернуть объект перечислителя для групп, например, для более простого API.

Из документации:

Этот метод является асинхронным. Когда перечисляются группы, пользователю может быть предложено подтвердить доступ приложения к данным; метод, однако, немедленно возвращается. Вы должны выполнить любую работу, которую хотите, с активами в enumerationBlock.

Я не уверен, почему вы пытаетесь выполнить с помощью последовательной очереди, но если вы просто хотите предотвратить одновременный доступ, тогда вы можете просто добавить переменную где-нибудь, которая отслеживает, в настоящее время перечисляем или нет, и сначала проверяем, если вам не нужно беспокоиться о проблемах синхронизации. (Если вы это делаете, возможно, вам следует изучить использование группы GCD, но это, вероятно, слишком велико для этой ситуации.)

+0

Причина, по которой я использую последовательную очередь/GCD, заключается в том, что без нее она блокирует пользовательский интерфейс при перечислении через картинки. Большая часть обработки изображений происходит от изменения размера до распознавания лица внутри этого блока. Когда телефон сначала загружает приложение, он вызывает этот метод. На заднем плане можно вызвать метод. Иногда эти события могут конфликтовать, поэтому я хотел создать последовательную очередь, так что, если событие начнется, а затем во время обработки событие будет вызвано снова, я могу поместить его в последовательную очередь для выполнения, когда это будет сделано. – inks2002

+0

В настоящее время я использую переменную BOOL, чтобы определить, перечислим ли мы в настоящее время. Я просто подумал, что использование последовательной очереди будет работать лучше. Но похоже, что этого не произойдет. См. Мою причину в разделе «ОБНОВЛЕНИЕ 2: ЧТО Я ДУМАЮ СЛУЧАЙ» в моем оригинальном посте. – inks2002

-1

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

Другой простая ошибка была бы создать не серийные очереди, но в параллельную очередь ...

0

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

Таким образом, вы должны обернуть все звонки внутри основного метода с явным dispatch_async(serializedQueue, ^{}), чтобы убедиться, что все делается в правильном порядке ...