数据库多线程问题

1.FMDB线程安全的实现

应用中不可在多个线程中共同使用一个FMDatabase对象操作数据库,这样会引起数据库数据混乱。如果要实现多线程,就需要使用FMDatabaseQueue。

FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithPath:[self getDatabasePath]];

用FMDatabaseQueue来创建队列,所有的操作都在block中执行。

//插入数据

    [queue inDatabase:^(FMDatabase *database) {

        //写入数据

        sql = @"insert into Test (name,image) values (?,?)";

        [database executeUpdate:sql,@"张三",data];

    }];

实现原理:在应用中只有一个FMDatabaseQueue实例,所有的任务都使用这一个共同的实例。FMDatabaseQueue其实是创建了一个gcd串行队列,放入队列中的block都串行执行,这就保证了同一时间队列中只执行一个操作,避免数据的混乱。

2.事务的概念

如果要保证多个操作同时成功或同时失败,可以把多个操作放到事务里。

[queue inTransaction:^(FMDatabase *db,BOOL*rollback) {

 [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:1]];

 [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:2]]; 

 [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:3]];

if(whoopsSomethingWrongHappened) { *rollback =YES;return;如果发生错误就进行回滚,把之前已经执行的任务都取消}// ...

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:4]];}];

3.coredata多线程

1.初始化moc(托管对象上下文,NSManagedObjectContext)的时候可以指定并发队列。

NSPrivateQueueConcurrencyType 私有并发队列类型,操作在子线程完成。

NSMainQueueConcurrencyType 主并发队列类型,涉及到UI相关操作应该使用这个队列。

2.调用方式,不能直接在不同的线程里使用同一个moc,会造成数据混乱:

- (void)performBlock:(void(^)())block 异步执行的block,调用之后会立刻返回。

- (void)performBlockAndWait:(void(^)())block 同步执行的block,调用之后会等待这个任务完成,才会继续向下执行。

比如说,在多线程的环境下执行MOC的save方法,就是将save方法放在MOC的block体中异步执行,其他方法的调用也是一样的。

[context performBlock:^{

    [context save:nil];异步执行save

}];⚠️:这种方法和我们之前做线程切换的方式是不一样的,不能自己开一个子线程然后到子线程里面去执行moc,应该是调用moc自身绑定的队列,把操作放到block里去执行。

iOS5之前使用多个moc:可能有多个MOC关联在同一个PSC上,当一个MOC发生改变并持久化到本地时,系统并不会将其他MOC缓存在内存中的NSManagedObject对象改变。所以这就需要我们在MOC发生改变时,将其他MOC数据更新。

多个moc关联同一个psc,在通知中心注册NSNotification。

// 创建主队列MOC,用于执行UI操作NSManagedObjectContext*mainMOC = [[NSManagedObjectContextalloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; mainMOC.persistentStoreCoordinator = PSC;

// 创建私有队列MOC,用于执行其他耗时操作NSManagedObjectContext*backgroundMOC = [[NSManagedObjectContextalloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; backgroundMOC.persistentStoreCoordinator = PSC;

// 通过监听NSManagedObjectContextDidSaveNotification通知,来获取所有MOC的改变消息 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];  

// MOC改变后的通知回调

- (void)contextChanged:(NSNotification *)noti {

NSManagedObjectContext *MOC = noti.object;

[MOC performBlock:^{ // 直接调用系统提供的同步API,系统内部会完成同步的实现细节。 [MOC mergeChangesFromContextDidSaveNotification:noti]; }];}

iOS5之前的数据同步:

简单数据冲突,一个moc数据发生改变,提交存储区,其他moc并没有对这个改变的数据进行更新。

有三种通知方式:

NSManagedObjectContextWillSaveNotification

NSManagedObjectContextDidSaveNotification

NSManagedObjectContextObjectsDidChangeNotification

监听到通知以后调用同步api,mergeChangesFromContextDidSaveNotification:noti

复杂数据同步:一个moc对本地数据的存储做出了改变,另一个moc也对相同的数据存储做了改变,这样在save的时候就产生了数据冲突。

这种情况下可以设置moc的mergePolicy属性来指定解决冲突的具体方案。一共有五种属性值。

NSErrorMergePolicy: 默认值,当出现合并冲突时,返回一个NSError对象来描述错误,而MOC和持久化存储区不发生改变

NSMergeByPropertyStoreTrumpMergePolicy:  以本地存储为准,使用本地存储来覆盖冲突部分。

NSMergeByPropertyObjectTrumpMergePolicy:  以MOC的为准,使用MOC来覆盖本地存储的冲突部分。

NSOverwriteMergePolicy:  以MOC为准,用MOC的所有NSManagedObject对象覆盖本地存储的对应对象。

NSRollbackMergePolicy:    以本地存储为准,MOC所有的NSManagedObject对象被本地存储的对应对象所覆盖。

iOS5之后使用多个moc:

关联了PSC的MOC是parentMOC,parentMOC执行save操作时真正将数据写入了本地数据库。每一个parentMOC都可以有多个childMOC,但是childMOC并不会关联parentMOC,childMOC执行save操作的时候并不会真正把数据写入数据库,而是会通知他的parentMOC自己发生了改变,由parentMOC完成写入数据库的任务。

// 创建PSC实例对象,还是用上面Demo的实例化代码

    NSPersistentStoreCoordinator *PSC = self.persistentStoreCoordinator;

// 创建主队列MOC,用于执行UI操作 NSManagedObjectContext *mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; mainMOC.persistentStoreCoordinator = PSC;  

// 创建私有队列MOC,用于执行其他耗时操作,backgroundMOC并不需要设置PSC NSManagedObjectContext *backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; backgroundMOC.parentContext = mainMOC; 

[backgroundMOC performBlock:^{

[backgroundMOC save:nil];

[mainMOC performBlock:^{

[mainMOC save:nil];//私有队列调用了save方法之后,主队列也要调用save方法,这样才能实现本地数据持久化。

}]; }]; }  

你可能感兴趣的:(数据库多线程问题)