使用嵌套的dispatch_sync / dispath_async处理死锁

时间:2014-12-15 05:08:05

标签: ios grand-central-dispatch uidocument dispatch-async

我最近在玩UIDocument并且在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];
        }];
    }];
}
});

结果也让我感到困惑,我在行[doc openWithCompletionHandler:^(BOOL success) {

上获得了EXC_BAD_INSTRUCTION

任何人都能解释一下引擎盖下的内容吗?这是我理解的,但显然是错误的。

This is what I understand, but it obviously wrong

1 个答案:

答案 0 :(得分:2)

我相信NoteDocument类派生自UIDocument class

<击> 所以-openWithCompletionHandler:在主队列上调用completionHandler。

  

-openWithCompletionHandler:参数:completionHandler

     

包含要在打开操作结束后执行的代码的块。

     

在主队列上调用该块。

另外我相信主队列(主线程)上的代码如下。

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

这意味着您打算等待在主队列中完成队列。是的,它会阻塞主队列,直到队列结束。

openWithCompletionHandler:已完成打开文档,然后将completionHandler块提交给主队列,但主队列已被dispatch_sync阻止。因此,永远不会调用completionHandler块。因此,&#34;测试&#34; queue永远不会完成,dispatch_sync也永远不会完成。

和EXC_BAD_INSTRUCTION,我怀疑-openWithCompletionHandler:可以从另一个线程调用。我不确定,但您可能需要从主线程调用该方法。

顺便说一下,你可能会误解dispatch_queue_create的第二个参数。

dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT)

第二个参数是 attr ,它不是队列的优先级。

  

BSD库函数手册dispatch_queue_create(3)

     

CREATION

     

attr参数指定要创建的队列类型,必须是   DISPATCH_QUEUE_SERIAL或DISPATCH_QUEUE_CONCURRENT。

幸运的是,在这种情况下,这不是问题。在&#39;&#39; /usr/include/dispatch/queue.h'&#39;&#39;

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0

相同
#define DISPATCH_QUEUE_SERIAL NULL

修改

如果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。