访问关系时核心数据privateQueue performBlockAndWait死锁 [英] Core Data privateQueue performBlockAndWait deadlock while accessing relationship

查看:96
本文介绍了访问关系时核心数据privateQueue performBlockAndWait死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

许多论坛都讨论了该主题,但是我仍然无法完全理解performBlockAndWait的实际工作方式.根据我的理解,context.performBlockAndWait(block: () -> Void)将在阻塞调用者线程的同时在其自己的队列中执行阻塞. 文档表示:

This topic has been discussed at many forum, but I am still not able to fully understand how performBlockAndWait actually works. As per my understanding, context.performBlockAndWait(block: () -> Void) will perform the block in its own queue while blocking the caller thread. Documentation says that:

您将标准"消息分组以发送到块内的上下文中 传递给其中一种方法.

You group "standard" messages to send to the context within a block to pass to one of these methods.

什么是标准"消息?它还说:

What are the "standard" messages? It also says that:

基于队列的受管对象上下文上的设置器方法是线程安全的. 您可以在任何线程上直接调用这些方法.

Setter methods on queue-based managed object contexts are thread-safe. You can invoke these methods directly on any thread.

这是否意味着我可以设置在performBlock * API外部的上下文的performBlock * API内部获取的托管对象的属性?

Does that mean that I can set properties of a managed object which is fetched inside performBlock* API of a context outside performBlock* APIs?

根据我的理解,在并发类型为.MainQueueConcurrencyType的上下文中调用performBlockAndWait(block: () -> Void)会在从主线程调用时创建死锁并永远阻止UI.但是在我的测试中,它不会造成任何死锁.

As per my understanding, calling performBlockAndWait(block: () -> Void) on context with concurrency type .MainQueueConcurrencyType will create a deadlock and block UI forever when called from main thread. But in my tests, its not creating any deadlock.

我认为它应该创建死锁的原因是,performBlockAndWait将首先阻塞调用者线程,然后在其自己的线程上执行该块.由于上下文必须在其中执行其块的线程与已经被阻塞的调用者线程相同,因此它将永远无法执行其块,并且该线程将永远保持阻塞状态.

The reason why I think that it should create a deadlock is that, performBlockAndWait will first block the caller thread, and then execute the block on its own thread. Since the thread in which context has to execute its block is the same as the caller thread which is already blocked, so it will never be able to execute its block and the thread remains blocked forever.

但是在某些奇怪的情况下,我面临僵局.我有以下测试代码:

However I am facing deadlocks in some weird scenario. I have below test code:

@IBAction func fetchAllStudentsOfDepartment(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: privateContext)
    let request = NSFetchRequest()
    request.entity = entity
    request.relationshipKeyPathsForPrefetching = ["students"]
    var department: Department?

    privateContext.performBlockAndWait { () -> Void in
        department = try! self.privateContext.executeFetchRequest(request).first as? Department
        print(department?.name)
        guard let students = department?.students?.allObjects as? [Student] else {
            return
        }
        for student in students {
            print(student.firstName)
        }
    }
}

@IBAction func fetchDepartment(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: privateContext)
    let request = NSFetchRequest()
    request.entity = entity

    privateContext.performBlockAndWait { () -> Void in
        let department = try! self.privateContext.executeFetchRequest(request).first as? Department
        print(department?.name)

    }

    privateContext.performBlockAndWait { () -> Void in
        let department = try! self.privateContext.executeFetchRequest(request).first as? Department
        print(department?.name)
    }
}

请注意,我不小心在测试代码的fetchDepartment方法中两次粘贴了performBlockAndWait.

Note that I accidentally pasted performBlockAndWait twice in fetchDepartment method in my test code.

  • 如果我没有打电话,它不会产生任何死锁 fetchAllStudentsOfDepartment方法.但是一旦我打电话 fetchAllStudentsOfDepartment,任何对fetchDepartment方法的调用 永远阻止用户界面.
  • 如果我在fetchAllStudentsOfDepartment方法中删除了print(student.firstName),则它不会阻塞.这意味着,仅当我访问关系的属性时,它才会阻止UI.
  • privateContextconcurrencyType设置为.PrivateQueueConcurrencyType.上面的代码仅在privateContextparentContextconcurrencyType设置为.MainQueueConcurrencyType时才阻止UI.

  • It does not create any deadlock if I have not called fetchAllStudentsOfDepartment method. But once I call fetchAllStudentsOfDepartment, any call to fetchDepartment method blocks the UI forever.
  • If I remove print(student.firstName) in fetchAllStudentsOfDepartment method, then it does not block. That means, it blocks UI only if I access a relationship's property.
  • privateContext has concurrencyType set to .PrivateQueueConcurrencyType. The above code blocks UI only when privateContext's parentContext has concurrencyType set to .MainQueueConcurrencyType.

我也已经与其他.xcdatamodel测试了相同的代码,现在我确定只有在访问关系的属性时,它才会阻塞.我当前的.xcdatamodel看起来像:

I have tested the same code with other .xcdatamodel as well and I am sure now that it only blocks if a relationship's property is accessed. My current .xcdatamodel looks like:

如果信息无关,请原谅我,但我只是在花费了大约8个小时之后才分享我的所有观察结果.阻止UI时,我可以发布线程堆栈.总而言之,我有三个问题:

Pardon me if the information is extraneous, but I am just sharing all my observations after spending like 8 hours already. I can post my thread stack when UI is blocked. To summarize, I have three questions:

  1. 什么是标准"消息?
  2. 我们可以设置在performBlock *之外的上下文的performBlock * API内获取的托管对象的属性吗?
  3. 为什么performBlockAndWait行为异常并导致我的测试代码出现UI阻塞.
  1. What are the "standard" messages?
  2. Can we set properties of a managed object which is fetched inside performBlock* API of a context outside performBlock*?
  3. Why performBlockAndWait is misbehaving and causing UI block in my test code.

测试代码::您可以从此处.

推荐答案

  1. 标准消息是旧的Objective-C语言.这意味着您应该对performBlockperformBlockAndWait中的ManagedObjectContext及其子ManagedObjects进行所有常规方法调用.在该块之外的专用上下文上唯一允许的调用是initsetParentContext.其他任何事情都应该在一个块中完成.

  1. Standard messages is old Objective-C lingo. That means you should do all of the regular method calls on a ManagedObjectContext and its child ManagedObjects in the performBlock or performBlockAndWait. The only calls that are allowed on a private context outside of the block is init and setParentContext. Anything else should be done in a block.

否.从私有上下文中获取的任何托管对象都只能在该私有上下文的队列中访问.从另一个队列访问(读取或写入)违反了线程限制规则.

No. Any managed object fetched from a private context must only be accessed on that private context's queue. Accessing (read or write) from another queue is violating the thread confinement rules.

出现阻塞问题的原因是因为您具有两个级别的"mainQueue"上下文,并且使"队列系统过分聪明".这是流程:

The reason you are having blocking issues is because you have two levels of "mainQueue" contexts and that is "outsmarting" the queue system. This is the flow:

  • 您在主队列上创建一个上下文,然后将其创建为另一个主队列上下文的子代.
  • 您创建该第二层主队列上下文的私有子项
  • 您以某种方式访问​​该专用队列上下文,以使其对当前已在主队列上下文中加载的对象产生故障.

由于主队列上下文有两个级别,因此会导致死锁,通常队列系统会看到潜在的死锁并避免死锁.

Because of the two levels of main queue contexts it is causing a deadlock where normally the queue system would see the potential deadlock and avoid it.

您可以通过将mainContext变量更改为:

You can test this by changing your mainContext variable to:

lazy var mainContext: NSManagedObjectContext = {
    let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
    return appDelegate!.managedObjectContext
}

您的问题消失了,因为队列系统将看到该块并避免该块.您甚至可以通过在performBlockAndWait()内放置一个断点来看到这种情况,并看到您仍在主队列中.

And your issue goes away because the queue system will see the block and avoid it. You can even see that happening by putting a break point inside of the performBlockAndWait() and see that you are still on the main queue.

最后,没有理由像父/子设计一样具有两个级别的主队列上下文.如果有的话,这是一个很好的论点,那就是不要这样做.

In the end, there is no reason to have two levels of main queue contexts like that in a parent/child design. If anything, this is a good argument NOT to do that.

我想念您已经在appDelegate中更改了模板代码,并将整个上下文转换为私有上下文.

I missed that you had altered the template code in the appDelegate and turned the overall context into a private one.

每个vc都有一个主MOC的模式会浪费很多Core Data的好处.有效的设计虽然在顶部有一个私有的,并且有一个主MOC(存在于整个应用程序中,而不仅仅是一个VC)是有效的设计,但是如果您在主队列中执行performBlockAndWait,则将不起作用.

That pattern of having a main MOC per vc throws away a lot of the benefits of Core Data. While having a private at the top and a main MOC (that exists for the entire app, not just one VC) is a valid design it won't work if you are doing performBlockAndWait like this from the main queue.

我不建议在主队列中使用performBlockAndWait,因为您要阻止整个应用程序.仅在调用 TO 主队列(或也许一个背景到另一个背景)时才使用performBlockAndWait.

I would not recommend ever using performBlockAndWait from the main queue as you are blocking the entire application. performBlockAndWait should only ever be used when calling TO the main queue (or perhaps one background to another background).

这篇关于访问关系时核心数据privateQueue performBlockAndWait死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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