gpt4 book ai didi

ios - 多线程的CoreData死锁

转载 作者:可可西里 更新时间:2023-11-01 05:45:25 34 4
gpt4 key购买 nike

我遇到了在多个 NSManagedObjectContexts 和多线程场景中发生的相同死锁问题(这在 SO 上很常见)。在我的一些 View Controller 中,我的应用程序使用后台线程从 Web 服务获取数据,并在同一个线程中保存数据。在其他情况下,在不保存的情况下不再继续前进是有意义的(例如,当他们点击“下一步”时从表单中保留值),保存在主线程上完成。 AFAIK 理论上这应该没有问题,但偶尔我可以在调用

时使死锁发生
if (![moc save:&error])

...当死锁发生时,这似乎总是在后台线程的保存中。并非每次通话都会发生这种情况;事实上恰恰相反,我必须使用我的应用程序几分钟,然后它就会发生。

我已经阅读了我能找到的所有帖子以及 Apple 文档等,我确信我正在遵循这些建议。具体来说,我对使用多个 MOC/线程的理解归结为:

  • 每个线程都必须有自己的 MOC。
  • 线程的 MOC 必须在该线程上创建(而不是从一个线程传递到另一个线程)。
  • 无法传递 NSManagedObject,但可以传递 NSManagedObjectID,并且您可以使用该 ID 使用不同的 MOC 扩充 NSManagedObject。
  • 如果一个 MOC 的更改都使用相同的 PersistentStoreCoordinator,则必须将它们合并到另一个 MOC。

不久前,我在 this SO thread 上看到了一些 MOC 辅助类的代码并发现它易于理解且使用起来非常方便,所以我现在所有的 MOC 交互都是通过它进行的。这是我的 ManagedObjectContextHelper 类的完整内容:

#import "ManagedObjectContextHelper.h"

@implementation ManagedObjectContextHelper

+(void)initialize {
[[NSNotificationCenter defaultCenter] addObserver:[self class]
selector:@selector(threadExit:)
name:NSThreadWillExitNotification
object:nil];
}

+(void)threadExit:(NSNotification *)aNotification {
TDAppDelegate *delegate = (TDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *threadKey = [NSString stringWithFormat:@"%p", [NSThread currentThread]];
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;

[managedObjectContexts removeObjectForKey:threadKey];
}

+(NSManagedObjectContext *)managedObjectContext {
TDAppDelegate *delegate = (TDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;

NSThread *thread = [NSThread currentThread];

if ([thread isMainThread]) {
[moc setMergePolicy:NSErrorMergePolicy];
return moc;
}

// a key to cache the context for the given thread
NSString *threadKey = [NSString stringWithFormat:@"%p", thread];

// delegate.managedObjectContexts is a mutable dictionary in the app delegate
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;

if ( [managedObjectContexts objectForKey:threadKey] == nil ) {
// create a context for this thread
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] init];
[threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
[threadContext setMergePolicy:NSErrorMergePolicy];
// cache the context for this thread
NSLog(@"Adding a new thread:%@", threadKey);
[managedObjectContexts setObject:threadContext forKey:threadKey];
}

return [managedObjectContexts objectForKey:threadKey];
}

+(void)commit {
// get the moc for this thread
NSManagedObjectContext *moc = [self managedObjectContext];

NSThread *thread = [NSThread currentThread];

if ([thread isMainThread] == NO) {
// only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
}

NSError *error;
if (![moc save:&error]) {
NSLog(@"Failure is happening on %@ thread",[thread isMainThread]?@"main":@"other");

NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
NSLog(@" DetailedError: %@", [detailedError userInfo]);
}
}
NSLog(@" %@", [error userInfo]);

}

if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:[self class] name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}

+(void)contextDidSave:(NSNotification*)saveNotification {
TDAppDelegate *delegate = (TDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;

[moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:NO];
}
@end

这里是多线程位的一个片段,它似乎死锁了:

NSManagedObjectID *parentObjectID = [parent objectID];

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
// GET BACKGROUND MOC
NSManagedObjectContext *backgroundContext = [ManagedObjectContextHelper managedObjectContext];

Parent *backgroundParent = (Parent*)[backgroundContext objectWithID:parentObjectID];
// HIT THE WEBSERVICE AND PUT THE RESULTS IN THE PARENT OBJECT AND ITS CHILDREN, THEN SAVE...
[ManagedObjectContextHelper commit];

dispatch_sync(dispatch_get_main_queue(), ^{

NSManagedObjectContext *mainManagedObjectContext = [ManagedObjectContextHelper managedObjectContext];

parent = (Parent*)[mainManagedObjectContext objectWithID:parentObjectID];
});
});

报错中的conflictList好像是跟父对象的ObjectID有关:

    conflictList =     (
"NSMergeConflict (0x856b130) for NSManagedObject (0x93a60e0) with objectID '0xb07a6c0 <x-coredata://B7371EA1-2532-4D2B-8F3A-E09B56CC04F3/Child/p4>'
with oldVersion = 21 and newVersion = 22
and old object snapshot = {\n parent = \"0xb192280 <x-coredata://B7371EA1-2532-4D2B-8F3A-E09B56CC04F3/Parent/p3>\";\n name = \"New Child\";\n returnedChildId = 337046373;\n time = 38;\n}
and new cached row = {\n parent = \"0x856b000 <x-coredata://B7371EA1-2532-4D2B-8F3A-E09B56CC04F3/Parent/p3>\";\n name = \"New Child\";\n returnedChildId = 337046373;\n time = 38;\n}"
);

我已经尝试在获得 MOC 后立即进行 refreshObject 调用,理论是如果这是我们之前使用过的 MOC(例如,我们之前和之前在主线程上使用了 MOC这很可能与帮助程序类将给我们的是同一个),那么也许在另一个线程中保存意味着我们需要显式刷新。但这没有任何区别,如果我一直点击足够长的时间,它仍然会死锁。

有没有人有什么想法?

编辑:如果我为所有异常设置了断点,则调试器会在 if (![moc save:&error]) 行自动暂停,因此播放/暂停按钮已经暂停并显示播放三角形。如果我禁用所有异常的断点,那么它实际上会记录冲突并继续 - 可能是因为合并策略当前设置为 NSErrorMergePolicy - 所以我认为它实际上不会在线程上死锁。 Here's a screehshot暂停时两个线程的状态。

最佳答案

我根本不推荐你的方法。首先,除非你局限于 iOS4,否则你应该使用 MOC 并发类型,而不是旧方法。即使在 iOS 5(嵌套上下文被破坏)下,performBlock 方法也更可靠。

另请注意,dispatch_get_global_queue 提供并发队列,不能用于同步。

详情可在此处找到:http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html

编辑

您正在尝试手动管理 MOC 和线程。如果你愿意,你可以做到,但你的道路上有龙。这就是创建新方法的原因,以最大限度地减少跨多个线程使用 Core Data 时出错的机会。每当我看到使用 Core Data 进行手动线程管理时,我总是会建议将其更改为第一种方法。这将立即消除大多数错误。

我不需要看到比您手动映射 MOC 和线程更多的信息就知道您是在自找麻烦。只需重新阅读该文档,并以正确的方式进行操作(使用 performBlock)。

关于ios - 多线程的CoreData死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16041081/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com