多线程操作数据库 (CoreData)

一夜长风 2011-12-14

1:主线程修改了数据库的某一条记录,但是子线程没有发生变化,反过来一样的问题。这种情况一般是发生在app有多个NSManagedObjectContext,两个线程分别对其进行了读写操作。

2:有时候程序会莫名其妙的crash掉,这个有很多原因:

a:有时候是因为两个线程同时读写数据库中的同一条记录。

b:有时候根本找不到是哪里的原因。

这两种情况一般是发生在app只有一个NSManagedObjectContext,两个线程都对其进行了读写操作。

在实际的开发当中,我遇到了各种各样的问题,如果是多线程操作数据库的话,个人建议:

1:最好一个线程对应一个NSManagedObjectContext。如果只有一个NSManagedObjectContext,并且多个线程对其进行操作,回出现许多不清不楚的问题。

2:在每一个线程对应一个NSManagedObjectContext的时候,尽量一个线程只读写与其对应的context。在其完成操作的时候通知另外的线程去修改其对应的context。在apple的api中有NSManagedObjectContextDidSaveNotification,它可以帮助你通知修改其它的contexts。

eg:[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(mergeContextChangesForNotification:)name:NSManagedObjectContextDidSaveNotificationobject:childThreadManagedObjectContext];

-(NSManagedObjectContext*)childThreadContext

{

if(childThreadManagedObjectContext!=nil)

{

returnchildThreadManagedObjectContext;

}

NSPersistentStoreCoordinator*coordinator=[selfpersistentStoreCoordinator];

if(coordinator!=nil)

{

childThreadManagedObjectContext=[[NSManagedObjectContextalloc]init];

[childThreadManagedObjectContextsetPersistentStoreCoordinator:coordinator];

}

else

{

DLog(@"createchildthreadmanagedobjectcontextfailed!");

}

[childThreadManagedObjectContextsetStalenessInterval:0.0];

[childThreadManagedObjectContextsetMergePolicy:NSOverwriteMergePolicy];

//////initentitydescription.

pChildThreadEntityDec=[NSEntityDescriptionentityForName:@"student"inManagedObjectContext:childThreadManagedObjectContext];

if(pChildThreadEntityDec==nil)

{

DLog(@"errorinitentitydescription!");

}

[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(mergeContextChangesForNotification:)name:NSManagedObjectContextDidSaveNotificationobject:childThreadManagedObjectContext];

returnchildThreadManagedObjectContext;

}

-(void)mergeOnMainThread:(NSNotification*)aNotification

{

[[selfmanagedObjectContext]mergeChangesFromContextDidSaveNotification:aNotification];

}

-(void)mergeContextChangesForNotification:(NSNotification*)aNotification

{

[selfperformSelectorOnMainThread:@selector(mergeOnMainThread:)withObject:aNotificationwaitUntilDone:YES];

}

上面的代码只是简单利用NSManagedObjectContextDidSaveNotification,当子线程修改了数据库以后,通知主线程去修改其对应的context。当然childThreadManagedObjectContext的创建是在创建子线程的时候进行的。

-(void)startChildThread

{

NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];

NSRunLoop*runLoop=[NSRunLoopcurrentRunLoop];

if([[manageDatabasesharedInstance]childThreadContext]==nil)

{

DLog(@"createchildthreadcontextfailed.");

}

[NSTimerscheduledTimerWithTimeInterval:10000.0target:selfselector:@selector(printCurrentData)userInfo:nilrepeats:YES];

[runLooprun];

[poolrelease];

}

注:实际上在什么时候创建都可以,只要保证在第一次用的之前创建好就可以了。上面是childThreadManagedObjectContext发生改变时,通知主context修改,所以在修改数据库时,一般都是修改childThreadManagedObjectContext。

eg:

-(void)saveDatabase:(student*)inObjectwithAge:(NSNumber*)inAge

{

if(inObject==nil||inAge==nil)

{

return;

}

NSError*pError;

if(self.age!=nil)

{

[self.agerelease];

self.age=nil;

}

self.age=[[NSNumberalloc]initWithInt:[inAgeintValue]];

if([NSThreadcurrentThread]==[NSThreadmainThread])

{

[selfperformSelector:@selector(saveWithChildThread:)onThread:[dataBaseAppDelegateshareDelegate].viewController.pShowController.pChildThreadwithObject:inObjectwaitUntilDone:NO];

}

else

{

inObject.stuAge=inAge;

if(![childThreadManagedObjectContextsave:&pError])

{

NSLog(@"savefailed:%@,%@",pError,[pErroruserInfo]);

}

}

}

-(void)saveWithChildThread:(student*)inStudent

{

NSError*pError;

NSManagedObjectID*tempObjectID=[inStudentobjectID];

student*tempStudet=(student*)[childThreadManagedObjectContextobjectWithID:tempObjectID];

tempStudet.stuAge=age;

if(![childThreadManagedObjectContextsave:&pError])

{

NSLog(@"savefailed:%@,%@",pError,[pErroruserInfo]);

}

}

这里修改数据库都是通过saveWithChildThread去修改的。当然上面if([NSThreadcurrentThread]==[NSThreadmainThread]),如果时在主线程也可以直接调用saveWithChildThread,而不用放到子线程修改,这里只是为了统一。每个context只在其对应的线程中修改。

相关推荐