更新我可以确认objectWithID
可能需要父(或祖父母等)上下文的线程来进行一些提取,因此请避免使用{{1}之类的内容阻止您的父线程}。
作为一个快速测试,我将孩子moc的父母指向了他们的祖父母,并让孩子的线程阻挡了原来的父母。在此设置中,永远不会发生死锁。这是一个糟糕的架构,所以我将重新架构。
原始问题
我有两层waitUntilAllOperationsAreFinished
。第一个是NSOperationQueue
图形,其操作在它们之间具有一组依赖关系。他们都运行良好,没有相互僵局。在其中一个操作(一组人员的调度程序)中,我将其工作分解为可以在另一个NSOperation
上运行的更多离散块。但是,我仍然希望Scheduler在考虑完成较大的操作之前完成所有的日程安排。为此,一旦我创建了所有Schedule操作并将它们添加到Scheduler操作队列,我就在操作队列上调用waitUntilAllOperationsAreFinished。这就是我陷入僵局的地方。
我正在使用Core Data并且有一个名为NSOperationQueue
的{{1}}子类,它处理获取父管理对象上下文的例程,创建PrivateQueueConcurrencyType子上下文,使用performBlockAndWait调用提供的块,最后等待在父上下文中合并更改。这是一些代码......
NSBlockOperation
这对我来说非常有用。但现在我想阻塞一个调用线程,直到它上面的操作队列完成所有操作。像这样......
BlockOperation
...这个帖子卡在最后一行(显然)。但我们从未看到其他线程在某一点上取得任何进展。暂停调试器我看到排队操作卡在哪里。它位于ScheduleOperation的init方法中,我们使用提供的id获取组。 (ScheduleOperation.scheduleGroupId调用此init)
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)
}
}
objectWithID是否需要在与其父moc关联的“父”线程上执行代码,从而产生死锁?我的方法还有什么可能导致这种情况吗?
注意:虽然我写的是Swift,但我已经将Objective-C添加为标记,因为我觉得这不是特定于语言的问题,而是特定于框架的问题。
答案 0 :(得分:1)
一般情况下,没有指定将调用哪个线程objectWithID
,这是一个实现细节。我在过去遇到了一些Core Data死锁的问题(虽然在不同的情况下),我发现当你在NSManagedObjectContext
上调用方法时,框架会在内部进行一些锁定。所以,是的,我认为这可能会导致僵局。
除了重新设计您的架构之外,我没有任何建议,也许它可以简化一点。请记住,您已经有一个与上下文关联的专用串行队列,这可以保证以指定的顺序调用操作。因此,您可以在所有ScheduleOperation
实例之间共享相同的上下文。将scheduleOperationQueue.maxConcurrentOperationsCount
设置为1,以便操作将一个接一个地执行。而不是阻塞调用线程,在最后一个操作完成时调用完成处理程序(您可以使用oepration的completionBlock
)。