更新,我可以确认objectWithID可能需要父(或祖父母等)上下文的线程来进行某些提取,因此请避免使用诸如waitUntilAllOperationsAreFinished之类的东西来阻塞父线程。

作为一项快速测试,我将孩子moc的父母指向他们的祖父母,然后让孩子线程阻塞了原来的父母。在此设置中,从未发生死锁。虽然这是一个糟糕的架构,所以我将重新设计。

原始问题

我有两层NSOperationQueue。第一个是NSOperation图,其操作之间具有一组依赖关系。它们都运行良好,彼此之间没有死锁。在这些操作之一(一组人的调度程序)中,我将其工作分解为可以在另一个NSOperationQueue上运行的更多离散块。但是,我仍然希望调度程序在认为较大的操作完成之前完成其所有调度的创建。为此,一旦我创建了所有Schedule操作并将它们添加到Scheduler操作队列中,就在操作队列上调用waitUntilAllOperationsAreFinished。这就是我的僵局。

我正在使用Core Data,并且有一个名为NSBlockOperationBlockOperation子类,该子类处理获取父托管对象上下文,创建PrivateQueueConcurrencyType子上下文,使用performBlockAndWait调用提供的块并最终在父上下文中合并更改的例程。这是一些代码...

init(block: (NSManagedObjectContext?) -> Void, withDependencies dependencies: Array<NSOperation>, andParentManagedObjectContext parentManagedObjectContext: NSManagedObjectContext?) {
    self.privateContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)

    super.init()

    self.queuePriority = NSOperationQueuePriority.Normal
    addExecutionBlock({
        if (parentManagedObjectContext != nil) {
            self.parentContext = parentManagedObjectContext!

            self.privateContext.parentContext = parentManagedObjectContext!

            self.privateContext.performBlockAndWait({ () -> Void in
                block(self.privateContext)
            })

            self.parentContext!.performBlockAndWait({ () -> Void in
                var error: NSError?
                self.parentContext!.save(&error)
            })
        }
    })

    for operation in dependencies {
        addDependency(operation)
    }
}


这对我来说真的很好。但是现在我想阻塞一个调用线程,直到该线程上的操作队列完成了所有操作为止。像这样...

for group in groups {
    let groupId = group.objectID
    let scheduleOperation = BlockOperation(
        block: { (managedObjectContext: NSManagedObjectContext?) -> Void in
            ScheduleOperation.scheduleGroupId(groupId, inManagedObjectContext: managedObjectContext!)
        },
        withDependencies: [],
        andParentManagedObjectContext: managedObjectContext)
    scheduleOperationQueue.addOperation(scheduleOperation)
}

scheduleOperationQueue.waitUntilAllOperationsAreFinished()


...该线程卡在最后一行(显然)。但是我们再也看不到其他线程在一定程度上取得了进展。暂停调试器,我看到排队的操作卡在了哪里。在ScheduleOperation的init方法中,我们使用提供的ID来获取组。 (ScheduleOperation.scheduleGroupId称为此init)

convenience init(groupId: NSManagedObjectID, inManagedObjectContext managedObjectContext: NSManagedObjectContext) {

    let group = managedObjectContext.objectWithID(groupId) as Group
    ...


objectWithID是否需要在与其父moc关联的“父”线程上执行代码,从而创建死锁?我的方法还有其他可能导致这种情况的原因吗?

注意:尽管我写的是Swift,但是我添加了Objective-C作为标签,因为我觉得这不是特定于语言的问题,而是特定于框架的问题。

最佳答案

通常,没有指定在哪个线程上调用objectWithID,这是一个实现细节。过去,我在使用Core Data死锁时遇到了一些问题(尽管在不同的情况下),我发现当您在NSManagedObjectContext上调用方法时,该框架在内部进行了一些锁定。所以是的,我认为这可能会导致僵局。

除了重新设计您的体系结构外,我没有其他建议,也许可以稍微简化一下。请记住,您已经有一个与上下文关联的专用串行队列,这可以确保将以指定的顺序调用操作。因此,您可以在所有ScheduleOperation实例之间共享相同的上下文。将scheduleOperationQueue.maxConcurrentOperationsCount设置为1,这样操作将一个接一个地执行。并且,除了阻塞调用线程之外,还可以在最后一个操作完成时调用完成处理程序(可以使用oepration的completionBlock)。

关于objective-c - waitUntilAllOperationsAreFinished和objectWithID,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27584789/

10-09 06:11