2014-12-15 4 views
1

Я играл с UIDocument недавно и путаю с понятием о dispatch_sync/dispath_asyncпроцесс тупиковый с вложенным dispatch_sync/dispath_async

У меня есть метод, который выборка всех документов информации

for (int i=0; i < noteDocuments.count; i++) {    
    NSURL * fileURL = [noteDocuments objectAtIndex:i]; 

    NoteDocument *doc = [[NoteDocument alloc] initWithFileURL:fileURL]; 
    [doc openWithCompletionHandler:^(BOOL success) { 
     [doc closeWithCompletionHandler:^(BOOL success) { 
      [self addOrUpdateInfoWithDoc:doc]; 
     }]; 
    }]; 
} 

Всё отлично работает с этот простой подход.

Тогда я пытаюсь dispatch_sync/dispath_async

Первый с dispath_sync

dispatch_sync(dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{ 
for (int i=0; i < noteDocuments.count; i++) {    
    NSURL * fileURL = [noteDocuments objectAtIndex:i]; 

    NoteDocument *doc = [[NoteDocument alloc] initWithFileURL:fileURL]; 
    [doc openWithCompletionHandler:^(BOOL success) { 
     [doc closeWithCompletionHandler:^(BOOL success) { 
      [self addOrUpdateInfoWithDoc:doc]; 
     }]; 
    }]; 
} 
}); 

После добавления dispatch_sync как этот метод никогда не закончить. Это меня смущает, потому что dispath_sync должен вернуться в основную очередь после того, как был выполнен блок, который в этом случае должен немедленно вернуться к openWithCompletionHandler.

Затем с любопытством меняю его dispatch_async (только для эксперимента)

dispatch_async(dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{ 
for (int i=0; i < noteDocuments.count; i++) {    
    NSURL * fileURL = [noteDocuments objectAtIndex:i]; 

    NoteDocument *doc = [[NoteDocument alloc] initWithFileURL:fileURL]; 
    [doc openWithCompletionHandler:^(BOOL success) { 
     [doc closeWithCompletionHandler:^(BOOL success) { 
      [self addOrUpdateInfoWithDoc:doc]; 
     }]; 
    }]; 
} 
}); 

Результат также путаешь меня, я EXC_BAD_INSTRUCTION на линии [doc openWithCompletionHandler:^(BOOL success) {

Может кто-нибудь объяснить, что происходит под капотом? Это то, что я понимаю, но это явно неправильно.

This is what I understand, but it obviously wrong

ответ

2

Я считаю, что NoteDocument класс является производным от UIDocument class.

So -openWithCompletionHandler: completeHandler вызывается в основной очереди.

-openWithCompletionHandler: Параметры: completionHandler

блок с кодом для выполнения после операции открытия заканчивается.

Блок вызывается в главной очереди.

Также я считаю, что следующий код в главной очереди (основной поток).

dispatch_sync(dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{ 
... 

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

openWithCompletionHandler: закончен, чтобы открыть документ, а затем он отправил блок завершенияHandler в основную очередь, но основная очередь была заблокирована dispatch_sync. Таким образом, блок executeHandler никогда не будет вызываться. Таким образом, очередь «тест» никогда не закончится, и dispatch_sync также никогда не завершится.

И EXC_BAD_INSTRUCTION, я сомневаюсь, что -openWithCompletionHandler: может быть вызван из другого потока. Я не уверен, но вам может потребоваться вызвать метод из основного потока.

Кстати, вы можете неправильно понять второй аргумент dispatch_queue_create.

dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT) 

2-й аргумент атр, это не приоритет очереди.

BSD Библиотека Функции Руководство dispatch_queue_create (3)

СОЗДАНИЯ

атр аргумент определяет тип очереди, чтобы создать и должны быть либо DISPATCH_QUEUE_SERIAL или DISPATCH_QUEUE_CONCURRENT.

К счастью, это не проблема в этом случае. В '' '/usr/include/dispatch/queue.h' ''

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 

такая же, как

#define DISPATCH_QUEUE_SERIAL NULL 

EDIT

Если UIDocument -openWithCompletionHandler: это обычный метод , это не вызовет DEADLOCK. Это будет следующая последовательность.

main queue     test queue 
    runloop 
    | 
    | 
dispatch_sync [your block] ----> 
(*BLOCKED*)    [your block] 
           | 
        UIDocument -openWithCompletionHandler: 
           | 
         (opening document...) 
           | 
         (finished to open) 
           | 
     <------------------ dispatch_async [the completion block] 
           | 
           DONE 
(Wake up from dispatch_sync 
because test queue was finished) 
    | 
    | 
the end of the runloop 
execute blocks 
    | 
[the completion block] 
    | 
    blocks done 
    | 
    | 
go to the top of the runloop 

Но, в соответствии с трассировкой стека LLDB, это немного отличается от другого метода.

#0 0x0358dfb6 in semaphore_wait_trap() 
#1 0x032504bf in _dispatch_thread_semaphore_wait() 
#2 0x03249e4b in _dispatch_barrier_sync_f_slow() 
#3 0x0324ee3a in _dispatch_barrier_sync_f() 
#4 0x0324a728 in _dispatch_barrier_sync_slow() 
#5 0x0324a61c in dispatch_barrier_sync() 
#6 0x00a9044b in -[UIDocument(UIDocumentInternal) _performBlock:synchronouslyOnQueue:]() 
#7 0x00a9048d in -[UIDocument(UIDocumentInternal) _performBlockSynchronouslyOnMainThread:]() 
#8 0x00a866a3 in -[UIDocument _registerAsFilePresenterIfNecessary]() 
#9 0x00a87891 in -[UIDocument openWithCompletionHandler:]() 

Таким образом, последовательность будет следующей.

main queue     test queue 
    runloop 
    | 
    | 
dispatch_sync [your block] ----> 
(*BLOCKED*)    [your block] 
           | 
        UIDocument -openWithCompletionHandler: 
           | 
        UIDocument _performBlockSynchronouslyOnMainThread: 
           | 
     <------------- dispatch_sync [UIDocument internal block] 
          (*BLOCKED*) 

Таким образом, DEADLOCK.

+0

Даже '- (void) openWithCompletionHandler: (void (^) (успех BOOL)) completeHandler' вызвал свой блок в главной очереди' dispatch_sync' уже был завершен, так как openWithCompletionHandler асинхронен правильно? – sarunw

+0

Э-э, я неправильно понял проблему. Обновлен ответ. –