1

我正在做 2 个核心数据提取,当它们大约同时执行时,它们会导致主线程死锁,从而导致我的应用程序冻结。

提取是在主线程上进行的。处理它的一种方法是确保不会在同一时间进行提取,但我无法控制何时进行提取。有没有其他方法可以避免这种僵局?

暂停应用程序时的堆栈跟踪是:

#0  0x9a5d191a in __psynch_mutexwait ()
#1  0x9166713b in pthread_mutex_lock ()
#2  0x01e5d591 in -[_PFLock lock] ()
#3  0x01e5d56a in -[NSPersistentStoreCoordinator lock] ()
#4  0x01e720ee in -[NSPersistentStoreCoordinator executeRequest:withContext:error:] ()
#5  0x01e70539 in -[NSManagedObjectContext executeFetchRequest:error:] ()
#6  0x0007cd62 in -[CoreDataHelper fetchEntity:predicate:andSortDescriptors:inManagedObjectContext:] at /xxx/CoreDataHelper.m:150
#7  0x000075f6 in -[RemindersListViewController refreshReminders] at /xxx/RemindersListViewController.m:64
#8  0x000082f8 in -[RemindersListViewController refreshGUI] at /xxx/RemindersListViewController.m:187
#9  0x00003735 in __58-[AppDelegate setTabCountAndScheduleRemindersInBackground]_block_invoke_2 at /xxx/AppDelegate.m:114
#10 0x022dc53f in _dispatch_call_block_and_release ()
#11 0x022ee014 in _dispatch_client_callout ()
#12 0x022de7d5 in _dispatch_main_queue_callback_4CF ()
#13 0x0261aaf5 in __CFRunLoopRun ()
#14 0x02619f44 in CFRunLoopRunSpecific ()
#15 0x02619e1b in CFRunLoopRunInMode ()
#16 0x02fd77e3 in GSEventRunModal ()
#17 0x02fd7668 in GSEventRun ()
#18 0x00d9dffc in UIApplicationMain ()
#19 0x0000297d in main at /xxx/main.m:16

编辑:访问核心数据的不同部分正在尝试执行获取请求。他们都在调用这个方法:

NSArray *reminders = [[CoreDataHelper sharedInstance] fetchEntity:APReminderEntity predicate:nil andSortDescriptors:[NSArray arrayWithObject:dateAscendingDescriptor] inManagedObjectContext:nil];

CoreDataHelper.m

- (NSArray *)fetchEntity:(NSString *)entity predicate:(NSPredicate *)predicate andSortDescriptors:(NSArray *)sortDescriptors inManagedObjectContext:(NSManagedObjectContext *)context {

    DLogName()

    if (context == nil) {

        // Use default MOC
        context = self.managedObjectContext;
    }

    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entity inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];

    if (predicate != nil) {

        [request setPredicate:predicate];
    }

    if (sortDescriptors != nil) {

        [request setSortDescriptors:sortDescriptors];
    }

    NSError *error = nil;
    NSArray *entities = [context executeFetchRequest:request error:&error];

    if (entities == nil) {        

        DLog(@"There was an error fetching entity: %@ Error: %@", entity, [error userInfo]);

        entities = [NSArray array];
    }

    return entities;
}

编辑 2:我只是尝试将我的 fetch 请求放在一个@synchronized块中(如本文中所建议的那样,到目前为止它似乎工作得很好......我正在正确的线程上访问我的 MOC.. MOC 被初始化为_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

4

2 回答 2

5

听起来问题出在您的应用程序的线程/互斥体/逻辑(或缺乏)周围想要同时使用核心数据的代码的依赖部分。

一个经常被忽视的线程规则:

在语言或 API 中使用线程结构并不一定意味着您的代码是线程安全的!

没有看到你的代码和逻辑,很难说更多。访问 Core Data 的不同部分是什么?

作为一般策略,消除死锁的一种方法可能是将有问题的核心数据访问代码收集到仅管理核心数据访问的控制器对象中。它可能负责避免您的代码引发的任何死锁情况。

关于一般事项:

NSManagedObjectContext在使用时锁定NSPersistentStoreCoordinator,因此只有一个 PSC 应该不是问题。

确保遵循 Core Data 中的线程规则。特别是,每一个NSManagedObjectContext都只能从一个线程访问——即创建它的线程。

有关更多讨论,请参见例如:

更新

我很确定您的问题是您NSManagedObjectContext从错误的线程访问。您必须从创建它的同一线程访问上下文。请验证您在代码中是否正确执行此操作。

你确定你是fetchEntity:从创建的同一个线程调用你的方法NSManagedObjectContext吗?该上下文是如何创建的,何时创建的?

看到这个问题和答案:NSManagedObjectContext Locked

于 2013-03-11T11:01:47.160 回答
0

我有相同的结果,但不同的问题。我有一个已经存在的 coredata 应用程序的多线程。它只使用主线程。

我根据我是否在主线程上来指定要使用的上下文。使用类似 dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND 可能会让你进入主线程(这是苹果优化)。这让我混合上下文对象并导致死锁

于 2016-05-03T19:37:05.807 回答