NSManagedObjectContext performBlockAndWait:不在后台线程上执行? [英] NSManagedObjectContext performBlockAndWait: doesn't execute on background thread?

查看:318
本文介绍了NSManagedObjectContext performBlockAndWait:不在后台线程上执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个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 在主线程而不是后台线程上执行任务

解决方案

抛开另一个答案, c $ c> performBlockAndWait 将始终在调用线程中运行。



performBlock 完全异步。它将总是将块排队到接收MOC的队列,然后立即返回。因此,

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

将在moc的队列中放置两个块。它们将始终异步执行。一些未知的线程将把块从队列中拉出并执行它们。另外,这些块被包裹在它们自己的自动释放池中,并且它们将表示完整的核心数据用户事件( processPendingChanges )。



performBlockAndWait 不使用内部队列。它是在调用线程的上下文中执行的同步操作。当然,它会等待队列上的当前操作被执行,然后该块将在调用线程中执行。



此外, performBockAndWait 是重入的,因此嵌套



Core Data工程师已经非常清楚,基于队列的MOC操作运行的实际线程并不重要。这是通过使用 performBlock * API的关键的同步。



因此,认为'performBlock'



performBlockAndWait 是此块将在某个未确定的时间,在这个完全相同的线程中执行该函数将在此代码完全执行后返回(将发生在当前队列之后







b

您确定performBlockAndWait不使用内部队列吗?
我认为它。唯一的区别是,performBlockAndWait将
等待,直到块完成。你是什​​么意思,调用
线程?在我的理解,[moc performBlockAndWait]和[moc
performBloc]都运行在其私有队列(背景或主)。
这里的重要概念是moc拥有队列,而不是其他方式
。如果我错了,请纠正我。 - Philip007


不幸的是,我的答案与我一样,因为它本身并不正确。然而,在原始问题的上下文是正确的。具体来说,当在私有队列上调用 performBlockAndWait 时,块将在调用该函数的线程上执行 - 它不会被放在队列上并在私有线程。



现在,在我进入细节之前,我想强调,根据库的内部工作原理是非常危险的。所有你应该关心的是,你永远不会期望一个特定的线程执行一个块,除了任何绑定到主线程。因此,期望在主线程上执行 performBlockAndWait 不是执行,因为它将在调用它的线程上执行。



performBlockAndWait 使用GCD,但它也有自己的层(例如,防止死锁)。如果你看看GCD代码(这是开源),你可以看到同步调用如何工作 - 一般来说,它们与队列同步,并调用线程上调用该函数的块 - 除非队列是主队列,或者全局队列。此外,在WWDC会议中,Core Data工程师强调, performBlockAndWait 将在调用线程中运行。



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



现在,SO不是一个好的论坛,因为它比这更复杂,特别是wrt主队列和GCD全局队列 - 但后者是不重要的核心数据。



要点是当你调用任何 performBlock * 或GCD函数时,它在任何特定的线程上运行(除了与主线程绑定的东西),因为队列不是线程,只有主队列在特定的线程上运行块。



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



我希望这很有意义,虽然它可能只是造成更多的混乱。



EDIT b

此外,您可以看到这一点的未说明的影响,因为 performBlockAndWait 提供了可重入的支持打破了块的FIFO顺序。作为例子...

  [context performBlockAndWait:^ {
NSLog(@One);
[context performBlock:^ {
NSLog(@Two);
}];
[context performBlockAndWait:^ {
NSLog(@Three);
}];
}];

请注意,严格遵守队列的FIFO保证意味着嵌套 performBlockAndWait (Three)将在异步块(Two)之后运行,因为它是在异步块提交之后提交的。然而,这不是什么事情,因为它是不可能的...同样的原因死锁随着嵌套 dispatch_sync 调用。



一般来说,尽可能避免同步版本,因为 dispatch_sync 可能会导致死锁,任何重入的版本,如 performBlockAndWait 将必须做一些坏决定支持它...像有同步版本跳队列。


I have an NSManagedObjectContext declared like so:

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

Notice that it is declared with a private queue concurrency type, so its tasks should be run on a background thread. I have the following code:

-(void)testThreading
{
    /* ok */
    [self.backgroundMOC performBlock:^{
        assert(![NSThread isMainThread]); 
    }];

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

Why does calling performBlockAndWait execute the task on the main thread rather than background thread?

解决方案

Tossing in another answer, to try an explain why performBlockAndWait will always run in the calling thread.

performBlock is completely asynchronous. It will always enqueue the block onto the queue of the receiving MOC, and then return immediately. Thus,

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

will place two blocks on the queue for moc. They will always execute asynchronously. Some unknown thread will pull blocks off of the queue and execute them. In addition, those blocks are wrapped within their own autorelease pool, and also they will represent a complete Core Data user event (processPendingChanges).

performBlockAndWait does NOT use the internal queue. It is a synchronous operation that executes in the context of the calling thread. Of course, it will wait until the current operations on the queue have been executed, and then that block will execute in the calling thread. This is documented (and reasserted in several WWDC presentations).

Furthermore, performBockAndWait is re-entrant, so nested calls all happen right in that calling thread.

The Core Data engineers have been very clear that the actual thread in which a queue-based MOC operation runs is not important. It's the synchronization by using the performBlock* API that's key.

So, consider 'performBlock' as "This block is being placed on a queue, to be executed at some undetermined time, in some undetermined thread. The function will return to the caller as soon as it has been enqueued"

performBlockAndWait is "This block will be executed at some undetermined time, in this exact same thread. The function will return after this code has completely executed (which will occur after the current queue associated with this MOC has drained)."

EDIT

Are you sure of "performBlockAndWait does NOT use the internal queue"? I think it does. The only difference is that performBlockAndWait will wait until the block's completion. And what do you mean by calling thread? In my understanding, [moc performBlockAndWait] and [moc performBloc] both run on its private queue (background or main). The important concept here is moc owns the queue, not the other way around. Please correct me if I am wrong. – Philip007

It is unfortunate that I phrased the answer as I did, because, taken by itself, it is incorrect. However, in the context of the original question it is correct. Specifically, when calling performBlockAndWait on a private queue, the block will execute on the thread that called the function - it will not be put on the queue and executed on the "private thread."

Now, before I even get into the details, I want to stress that depending on internal workings of libraries is very dangerous. All you should really care about is that you can never expect a specific thread to execute a block, except anything tied to the main thread. Thus, expecting a performBlockAndWait to not execute on the main thread is not advised because it will execute on the thread that called it.

performBlockAndWait uses GCD, but it also has its own layer (e.g., to prevent deadlocks). If you look at the GCD code (which is open source), you can see how synchronous calls work - and in general they synchronize with the queue and invoke the block on the thread that called the function - unless the queue is the main queue or a global queue. Also, in the WWDC talks, the Core Data engineers stress the point that performBlockAndWait will run in the calling thread.

So, when I say it does not use the internal queue, that does not mean it does not use the data structures at all. It must synchronize the call with the blocks already on the queue, and those submitted in other threads and other asynchronous calls. However, when calling performBlockAndWait it does not put the block on the queue... instead it synchronizes access and runs the submitted block on the thread that called the function.

Now, SO is not a good forum for this, because it's a bit more complex than that, especially w.r.t the main queue, and GCD global queues - but the latter is not important for Core Data.

The main point is that when you call any performBlock* or GCD function, you should not expect it to run on any particular thread (except something tied to the main thread) because queues are not threads, and only the main queue will run blocks on a specific thread.

When calling the core data performBlockAndWait the block will execute in the calling thread (but will be appropriately synchronized with everything submitted to the queue).

I hope that makes sense, though it probably just caused more confusion.

EDIT

Furthermore, you can see the unspoken implications of this, in that the way in which performBlockAndWait provides re-entrant support breaks the FIFO ordering of blocks. As an example...

[context performBlockAndWait:^{
    NSLog(@"One");
    [context performBlock:^{
        NSLog(@"Two");
    }];
    [context performBlockAndWait:^{
        NSLog(@"Three");
    }];
}];

Note that strict adherence to the FIFO guarantee of the queue would mean that the nested performBlockAndWait ("Three") would run after the asynchronous block ("Two") since it was submitted after the async block was submitted. However, that is not what happens, as it would be impossible... for the same reason a deadlock ensues with nested dispatch_sync calls. Just something to be aware of if using the synchronous version.

In general, avoid sync versions whenever possible because dispatch_sync can cause a deadlock, and any re-entrant version, like performBlockAndWait will have to make some "bad" decision to support it... like having sync versions "jump" the queue.

这篇关于NSManagedObjectContext performBlockAndWait:不在后台线程上执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆