ios – 具有多个线程的CoreData死锁

ios – 具有多个线程的CoreData死锁,第1张

概述我遇到了多个NSManagedObjectContexts&多线程场景.在我的一些视图控制器中,我的应用程序使用后台线程从Web服务获取数据,并在同一个线程中保存它.在其他情况下,在没有保存的情况下不进一步进展是有意义的(例如,当他们点击“下一步”时表单中的持久值),保存在主线程上完成. AFAIK在理论上应该没有任何问题,但偶尔我可以在调用时发生死锁 if (![moc save:&error] 我遇到了多个NSManagedobjectContexts&多线程场景.在我的一些视图控制器中,我的应用程序使用后台线程从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死锁全部内容,希望文章能够帮你解决ios – 具有多个线程的CoreData死锁所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1039798.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-24
下一篇 2022-05-24

发表评论

登录后才能评论

评论列表(0条)

保存