我有一个NSManagedObjectContext声明如下:

- (NSManagedObjectContext *) backgroundMOC {
    if (backgroundMOC != nil) {
        return backgroundMOC;
    }
    backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    return backgroundMOC;
}

请注意,它是使用专用队列并发类型声明的,因此其任务应在后台线程上运行。我有以下代码:
-(void)testThreading
{
    /* ok */
    [self.backgroundMOC performBlock:^{
        assert(![NSThread isMainThread]);
    }];

    /* CRASH */
    [self.backgroundMOC performBlockAndWait:^{
        assert(![NSThread isMainThread]);
    }];
}

为什么调用performBlockAndWait在主线程而不是后台线程上执行任务?

最佳答案

抛出另一个答案,尝试解释为什么performBlockAndWait将始终在调用线程中运行。
performBlock是完全异步的。它将始终将块排队到接收MOC的队列中,然后立即返回。因此,

[moc performBlock:^{
    // Foo
}];
[moc performBlock:^{
    // Bar
}];

将两个块放在moc队列上。它们将始终异步执行。某些未知线程会将块从队列中拉出并执行。此外,这些块被包装在它们自己的自动释放池中,并且它们还将代表完整的Core Data用户事件(processPendingChanges)。
performBlockAndWait不使用内部队列。它是在调用线程的上下文中执行的同步操作。当然,它将等待直到队列上的当前操作已执行,然后该块将在调用线程中执行。对此进行了记录(并在多个WWDC演示文稿中得到了重申)。

此外,performBockAndWait是可重入的,因此嵌套调用都恰好在该调用线程中发生。

核心数据工程师已经非常清楚,基于队列的MOC操作在其中运行的实际线程并不重要。这是使用performBlock* API进行同步的关键。

因此,将'performBlock'视为“此块被放置在队列中,将在某个不确定的时间在某个不确定的线程中执行。该函数将在排队后立即返回给调用者”
performBlockAndWait是“此块将在这个完全相同的线程中在某个不确定的时间执行。该函数将在此代码完全执行后返回(此MOC关联的当前队列耗尽后将发生)。”

编辑



不幸的是,我像我一样回答了这个问题,因为就其本身而言,这是不正确的。但是,在原始问题的上下文中,它是正确的。具体来说,当在专用队列上调用performBlockAndWait时,该块将在调用该函数的线程上执行-不会将其放在队列上并在“专用线程”上执行。

现在,在我未详细介绍之前,我想强调一下,依靠库的内部运作是非常危险的。您真正要关心的是,除了与主线程相关的任何内容外,您永远都不会期望特定线程执行一个块。因此,建议不要让performBlockAndWait在主线程上不执行,因为它将在调用它的线程上执行。
performBlockAndWait使用GCD,但它也有自己的层(例如,防止死锁)。如果您查看GCD代码(开放源代码),则可以看到同步调用的工作原理-通常,它们与队列同步并在调用该函数的线程上调用该块-除非该队列是主队列或全局队列。另外,在WWDC对话中,核心数据工程师强调了performBlockAndWait将在调用线程中运行的观点。

因此,当我说它不使用内部队列时,这并不意味着它根本不使用数据结构。它必须使调用与队列中已经存在的块以及在其他线程和其他异步调用中提交的块同步。但是,在调用performBlockAndWait时,它不会将块放在队列中……而是同步访问并在调用该函数的线程上运行提交的块。

现在,SO并不是一个很好的论坛,因为它要复杂得多,特别是没有主队列和GCD全局队列-但是后者对于Core Data并不重要。

要点是,当您调用任何performBlock*或GCD函数时,您不应期望它在任何特定线程上运行(与主线程无关的事物除外),因为队列不是线程,只有主队列将在线程上运行块。特定线程。

当调用核心数据performBlockAndWait时,该块将在调用线程中执行(但将与提交到队列的所有内容适当地同步)。

我希望这是有道理的,尽管这可能只会引起更多的困惑。

编辑

此外,您还可以看到其隐含的含义,因为performBlockAndWait提供可重入支持的方式打破了块的FIFO顺序。举个例子...
[context performBlockAndWait:^{
    NSLog(@"One");
    [context performBlock:^{
        NSLog(@"Two");
    }];
    [context performBlockAndWait:^{
        NSLog(@"Three");
    }];
}];

请注意,严格遵守队列的FIFO保证将意味着嵌套的performBlockAndWait(“三”)将在异步块(“二”)之后运行,因为它是在提交异步块之后提交的。但是,这不会发生,因为这是不可能的……出于同样的原因,嵌套dispatch_sync调用会导致死锁。如果使用同步版本,只有一点要注意。

通常,应尽可能避免使用同步版本,因为dispatch_sync可能会导致死锁,并且任何可重入版本(例如performBlockAndWait)都必须做出一些“糟糕”的决定来支持它……就像使同步版本“跳过”队列一样。

关于iphone - NSManagedObjectContext performBlockAndWait : doesn't execute on background thread?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11831946/

10-13 03:57