核心数据被多线程锁定(_objectStoreLockCount> 1)

时间:2013-05-14 21:45:51

标签: objective-c xcode macos cocoa core-data

我发现修复核心数据DB锁定情况很困难(当我在调试模式下运行时,_objectStoreLockCount计数> 1)。

以下是我的应用程序的架构(MAC OSX):

  1. 我正在使用2个NSOperationQueue来执行一些活动。每个队列有20个并行运行的活动。
  2. 完成这些活动后,每个活动都将结果数据放入另一个NSOperationQueue中,其最大并发操作数设置为1。
  3. 然后,结果操作队列将数据放入核心数据中。
  4. 还有2个其他线程在运行。其中之一是从核心数据中读取数据。另一个基于某种逻辑的是更新核心数据中的现有记录。
  5. 对于核心数据处理,我创建了一个具有3个对象的NSObject子类。每个对象都有自己的NSManagedObjectContext - 1用于读取数据,1用于写入新记录,1用于更新现有记录。每当写入管理对象上下文发生更改时,我都会更新读取的托管对象上下文。所有三个MOC都有共同的持久性商店协调员。
  6. 应用程序应该运行24 * 7以上所有代码都必须重复
  7. 问题:当我运行我的应用程序时,它会随机挂起或锁定数据库。它随机时间达到这个状态 - 2小时,10小时未定义......

    我正在附加应用程序的核心数据处理部分。如果我在核心数据处理方面遗漏了一些内容,请告诉我:

    @implementation MyAppDataRepository
    
    @synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
    @synthesize managedObjectModel = _managedObjectModel;
    @synthesize writeManagedObjectContext = _writeManagedObjectContext;
    @synthesize readManagedObjectContext = _readManagedObjectContext;
    @synthesize updateManagedObjectContext = _updateManagedObjectContext;
    
    static  MyAppDataRepository *sharedMyAppWriteDataRepository = nil;
    static  MyAppDataRepository *sharedMyAppReadDataRepository = nil;
    static  MyAppDataRepository *sharedMyAppupdateDataRepository = nil;
    
    + (MyAppDataRepository *)sharedMyAppWriteDataRepositoryMyApp
    {
        static dispatch_once_t pred = 0;
        dispatch_once(&pred, ^{
            sharedMyAppWriteDataRepository = [[self alloc] init];
            [sharedMyAppWriteDataRepository persistentStoreCoordinator];
            [sharedMyAppWriteDataRepository writeManagedObjectContext];
        });
    
        return sharedMyAppWriteDataRepository;
    }
    
    + (MyAppDataRepository *)sharedMyAppReadDataRepositoryMyApp
    {
        static dispatch_once_t pred = 0;
        dispatch_once(&pred, ^{
            sharedMyAppReadDataRepository = [[self alloc] init]; // or some other init method
            [sharedMyAppReadDataRepository persistentStoreCoordinator];
            [sharedMyAppReadDataRepository readManagedObjectContext];
        });
    
        return sharedMyAppReadDataRepository;
    }
    
    
    + (MyAppDataRepository *)sharedMyAppupdateDataRepositoryMyApp
    {
        static dispatch_once_t pred = 0;
        dispatch_once(&pred, ^{
            sharedMyAppupdateDataRepository = [[self alloc] init]; // or some other init method
            [sharedMyAppupdateDataRepository persistentStoreCoordinator];
            [sharedMyAppupdateDataRepository  updateManagedObjectContext];
        });
    
        return sharedMyAppupdateDataRepository;
    }
    
    
    -(id)init {
        if ((self = [super init])) {
            if (!self.writeManagedObjectContext) {
                MyAppLOG(@"Core data cannot be initiated");
            }
        }
        return  self;
    }
    
    // Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.apple.retail.MyApp" in the user's Application Support directory.
    - (NSURL *)applicationFilesDirectory
    {
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
        return [appSupportURL URLByAppendingPathComponent:@"com.apple.retail.MyApp"];
    }
    
    // Creates if necessary and returns the managed object model for the application.
    - (NSManagedObjectModel *)managedObjectModel
    {
        if (_managedObjectModel) {
            return _managedObjectModel;
        }
    
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        return _managedObjectModel;
    }
    
    // Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.)
    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    {
        if (_persistentStoreCoordinator) {
            return _persistentStoreCoordinator;
        }
    
        NSManagedObjectModel *mom = [self managedObjectModel];
        if (!mom) {
            MyAppLOG(@"%@:%@ No model to generate a store from", [self class], NSStringFromSelector(_cmd));
            return nil;
        }
    
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
        NSError *error = nil;
    
        NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:@[NSURLIsDirectoryKey] error:&error];
    
        if (!properties) {
            BOOL ok = NO;
            if ([error code] == NSFileReadNoSuchFileError) {
                ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
            }
            if (!ok) {
                [[NSApplication sharedApplication] presentError:error];
                return nil;
            }
        } else {
            if (![properties[NSURLIsDirectoryKey] boolValue]) {
                // Customize and localize this error.
                NSString *failureDescription = [NSString stringWithFormat:@"Expected a folder to store application data, found a file (%@).", [applicationFilesDirectory path]];
    
                NSMutableDictionary *dict = [NSMutableDictionary dictionary];
                [dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
                error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
    
                [[NSApplication sharedApplication] presentError:error];
                return nil;
            }
        }
    
        //Support for automatic migration of core data
        NSMutableDictionary *optionsDictionary = [NSMutableDictionary dictionary];
        [optionsDictionary setObject:[NSNumber numberWithBool:YES]
                              forKey:NSMigratePersistentStoresAutomaticallyOption];
        [optionsDictionary setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
    
        NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:@"MyApp.sqlite"];
        NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
        if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:optionsDictionary error:&error]) {
            [[NSApplication sharedApplication] presentError:error];
            return nil;
        }
        _persistentStoreCoordinator = coordinator;
    
        return _persistentStoreCoordinator;
    }
    
    
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
    - (NSManagedObjectContext *)writeManagedObjectContext {
        if (_writeManagedObjectContext) {
            return _writeManagedObjectContext;
        }
    
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        if (!coordinator) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            [dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
            [dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
            NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
            [[NSApplication sharedApplication] presentError:error];
            return nil;
        }
        _writeManagedObjectContext = [[NSManagedObjectContext alloc] init];
        [_writeManagedObjectContext setPersistentStoreCoordinator:coordinator];
    
        return _writeManagedObjectContext;
    }
    
    
    - (NSManagedObjectContext *)readManagedObjectContext {
        if (_readManagedObjectContext) {
            return _readManagedObjectContext;
        }
    
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        if (!coordinator) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            [dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
            [dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
            NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
            [[NSApplication sharedApplication] presentError:error];
            return nil;
        }
        _readManagedObjectContext = [[NSManagedObjectContext alloc] init];
        [_readManagedObjectContext setPersistentStoreCoordinator:coordinator];
    
        return _readManagedObjectContext;
    }
    
    
    - (NSManagedObjectContext *)updateManagedObjectContext {
        if (_updateManagedObjectContext) {
            return _updateManagedObjectContext;
        }
    
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        if (!coordinator) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            [dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
            [dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
            NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
            [[NSApplication sharedApplication] presentError:error];
            return nil;
        }
        _updateManagedObjectContext = [[NSManagedObjectContext alloc] init];
        [_updateManagedObjectContext setPersistentStoreCoordinator:coordinator];
    
        return _updateManagedObjectContext;
    }
    
    
    // Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application.
    - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
        return [[self writeManagedObjectContext] undoManager];
    }
    
    // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user.
    - (IBAction)saveAction:(id)sender
    {
        NSError *error = nil;
    
        if (![[self writeManagedObjectContext] commitEditing]) {
            MyAppLOG(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
        }
    
        if (![[self writeManagedObjectContext] save:&error]) {
            [[NSApplication sharedApplication] presentError:error];
        }
    }
    
    
    - (BOOL)saveDataInDatabase:(NSManagedObjectContext *)iContext {
        BOOL dataSavedSuccessfully = YES;
    
        NSError *error = nil;
    
        if (![iContext commitEditing]) {
            MyAppLOG(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
        }
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:iContext];
    
    
        if (![iContext save:&error]) {
            dataSavedSuccessfully = NO;
            [[NSApplication sharedApplication] presentError:error];
        }
    
        // unregister from notification
        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:iContext];
    
        return dataSavedSuccessfully;
    }
    
    
    // Merge the changes from write managed object context to read managed object context
    - (void)handleDidSaveNotification:(NSNotification *)iNotification {
        [[MyAppDataRepository sharedMyAppReadDataRepository].readManagedObjectContext mergeChangesFromContextDidSaveNotification:iNotification];
    }
    
    
    - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
    {
        // Save changes in the application's managed object context before the application terminates.
    
        if (!_writeManagedObjectContext) {
            return NSTerminateNow;
        }
    
        if (![[self writeManagedObjectContext] commitEditing]) {
            MyAppLOG(@"%@:%@ unable to commit editing to terminate", [self class], NSStringFromSelector(_cmd));
            return NSTerminateCancel;
        }
    
        if (![[self writeManagedObjectContext] hasChanges]) {
            return NSTerminateNow;
        }
    
        NSError *error = nil;
        if (![[self writeManagedObjectContext] save:&error]) {
    
            // Customize this code block to include application-specific recovery steps.
            BOOL result = [sender presentError:error];
            if (result) {
                return NSTerminateCancel;
            }
    
            NSString *question = NSLocalizedString(@"Could not save changes while quitting. Quit anyway?", @"Quit without saves error question message");
            NSString *info = NSLocalizedString(@"Quitting now will lose any changes you have made since the last successful save", @"Quit without saves error question info");
            NSString *quitButton = NSLocalizedString(@"Quit anyway", @"Quit anyway button title");
            NSString *cancelButton = NSLocalizedString(@"Cancel", @"Cancel button title");
            NSAlert *alert = [[NSAlert alloc] init];
            [alert setMessageText:question];
            [alert setInformativeText:info];
            [alert addButtonWithTitle:quitButton];
            [alert addButtonWithTitle:cancelButton];
    
            NSInteger answer = [alert runModal];
    
            if (answer == NSAlertAlternateReturn) {
                return NSTerminateCancel;
            }
        }
    
        return NSTerminateNow;
    }
    
    
    - (void)insertTestResults:(NSArray *)iTestData withUpdateFlag:(int)iUpdateFlag withDiscardDlag:(int)iDiscardFlag {
        NSManagedObjectContext *aWriteManagedObjContext = [[MyAppDataRepository sharedMyAppWriteDataRepositoryMyApp] writeManagedObjectContext];
    
        NSManagedObject *aTestResults;
        NSMutableArray *aTestIDArray=[NSMutableArray array];
    
        for (MyAppTestResultsModel *iTestResultsData in iTestData) {
            NSString *aTestType=[iTestResultsData valueForKey:kMyAppTestType];
    
            if ([aTestType isEqualToString:kMyAppTest1Type]) {
                aTestResults=[NSEntityDescription
                              insertNewObjectForEntityForName:kMyAppTest1ResultsEntity
                              inManagedObjectContext:aWriteManagedObjContext];
            } else if ([aTestType isEqualToString:kMyAppTest2Type]) {
                aTestResults=[NSEntityDescription
                              insertNewObjectForEntityForName:kMyAppTest2ResultsEntity
                              inManagedObjectContext:aWriteManagedObjContext];
            } else if ([aTestType isEqualToString:kMyAppTest3Type]) {
                aTestResults=[NSEntityDescription
                              insertNewObjectForEntityForName:kMyAppTest3ResultsEntity
                              inManagedObjectContext:aWriteManagedObjContext];
            } else if ([aTestType isEqualToString:kMyAppTest4Type]) {
                aTestResults=[NSEntityDescription
                              insertNewObjectForEntityForName:kMyAppTest4ResultsEntity
                              inManagedObjectContext:aWriteManagedObjContext];
            }
    
            NSError *anError=nil;
            if (![self saveDataInDatabase:aWriteManagedObjContext]) {
                MyAppLOG(@"Cannot Save!: %@", [anError localizedDescription]);
            }
            else
            {
                // Post the saved Message to the main window
                NSString *aLog = [NSString stringWithFormat:@"\n \n%@ Test Results Saved \n %@",[iTestResultsData valueForKey:kMyAppTestType], aTestResults];
                MyAppLOG(@"%@",aLog);
                MyAppINFOLOG(@"Saved test results (in client DB) for Test ID = %@ Test Type = %@ App ID = %@ Network = %@ Status = %@", [iTestResultsData valueForKey:kMyAppTestID], [iTestResultsData valueForKey:kMyAppTestType], [iTestResultsData valueForKey:kMyAppAppIDAttribute], [iTestResultsData valueForKey:kMyAppTestNetwork], [iTestResultsData valueForKey:kMyAppTestStatusAttribute]);
    
                [aTestIDArray addObject:[aTestResults valueForKey:kMyAppTestIDAttribute]];
    
                // Update the isReadyToFlag
                if (iUpdateFlag == 1)
                    [MyAppFetchTestConfigDetails updateReadyToRunForTestID:[NSArray arrayWithArray:aTestIDArray] withInt:iUpdateFlag];
            }
        }
    }
    

    下面是线程2的操作队列实现(已由多个线程填充):

    @implementation MyAppSaveTestResultController
    
    - (id)init {
        if ((self = [super init]) != nil) {
            self.queue = [[NSOperationQueue alloc] init];
            [self.queue setMaxConcurrentOperationCount:1];
        }
        return self;
    }
    
    + (MyAppSaveTestResultController *)sharedSaveResultsController {
        static dispatch_once_t pred = 0;
        dispatch_once(&pred, ^{
            sharedSaveResultsController = [[self alloc] init]; // or some other init method
        });
        return sharedSaveResultsController;
    }
    
    - (void)startSaveOperation {
        MyAppSaveTestResultsOperation *aSaveOperation = [[MyAppSaveTestResultsOperation alloc] initWithTestResults:self.testResults updateFlag:self.updateFlag andDiscardDlag:self.discardFlag];
        [self.queue addOperation:aSaveOperation];
    
    }
    
    
    @implementation MySaveTestResultsOperation
    
    - (id)initWithTestResults:(NSArray *)iTestData updateFlag:(int)iUpdateFlag andDiscardDlag:(int)iDiscardFlag {
        if ((self = [super init]) != nil)
        {
            self.testResults = iTestData;
            self.updateFlag = iUpdateFlag;
            self.discardFlag = iDiscardFlag;
        }
    
        return self;
    }
    
    - (void)start {
        if (![NSThread isMainThread])
        {
            [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
            return;
        }
    
        // Check for cancellation
        if ([self isCancelled]) {
            [self completeOperation];
            return;
        }
    
        // Executing
        [self willChangeValueForKey:@"isExecuting"];
        executing = YES;
        [self didChangeValueForKey:@"isExecuting"];
    
        // Begin
        [self beginOperation];
    }
    
    - (void)beginOperation {
        @try {
            [[MyAppDataRepository sharedMyAppWriteDataRepository] insertTestResults:results withUpdateFlag:self.updateFlag withDiscardDlag:self.discardFlag];
            [self completeOperation];
        } @catch(NSException * e) {
        }
    }
    

    从所有活动线程(多达20个并发线程)中调用下面的代码,将数据填入MyAppSaveTestResultController:

    MyAppSaveTestResultController *aSaveTestResultController = [MyAppSaveTestResultController sharedSaveResultsController];
        [aSaveTestResultController saveTestResults:[NSArray arrayWithObject:data] updateFlag:[[NSNumber numberWithBool:[aBlockSelf checkPendingTestsForTestID:anID]] intValue] discardFlag:0];
        [aSaveTestResultController startSaveOperation];
    

0 个答案:

没有答案