FMDB和CoreData在多线程中的应用
FMDB在多线程中应用
如果包装的是FMDatabase类,就绝对会有问题, 因为FMDatabase实例不能在多线程环境共享,在多线程中决不能使用一个FMDatabase实例, 如果在线程使用单独的FMDatabase实例是允许的,但是同样可能发生database is locked的问题,这是由于多线程对sqlite的竞争引起的.
在多线程中使用时要使用FMDatabaseQueue, 全局只需一个queue,让每一个线程使用同一个Queue的实例, ‘queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];’,
FMDatabaseQueue 看似是一个队列, 其实并不是,它是通过内部创建一个Serial的 ‘idspatch_queue_t’ 来处理通过 ‘inDatabase’和’inTransaction’ 传入的Blocks,所以我们在主线程或者后台用这两种方式来调用都是同步的, 设置一个Serial队列后,FMDBBaseQueue就变成线程安全的,所有的数据库访问都是同步执行的,这比@synchronized后者NSLock要高效的多.
- 如果在后台执行大量的更新,而主线程也需要访问数据库,这样就会等待后台,阻塞主线程,对此有一些想法:
1.如果实在后台使用inDatabase来执行更新,可以考虑换成’inTransaction’,后者要比前者快很多,特别是上千.上万级别的数据量
2.可以拆分任务,1000条数据可以分多次执行,每次更新一些,这个要看业务,来选择这个方式
3.以上两点可以解决问题,在大多时候,应该把从主线程调用’inDatabase’和’inTransaction’放在异步里面:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.databaseQueue inDatabase:^(FMDatabase *db) {
//do something...
}];
});'
*如果在插入大量数据时候 ,其中一天数据库插入失败, 则也可以启用beginTransaction
,在成功时候commitTransaction
, 失败时候:rollbackTransaction
- 注: 以上多线程操作, 能解决大量数据操作不依赖于数据库返回的结果的情况,如果对返回结果有依赖,就需要考虑UI上的体验了,如加一个 ‘UIActivityIndicatorView’.
CoreData在多线程中应用
- 第一步搭建多线程环境
CoreData对于并发模式的支持非常完备,NSManagedoBjectContext的指定初始化方法中就制定了并发模式:
init(concurrencyType ct:NSManagedObjectContextConcurrencyType)
1.NSConfinementConcurrencyType
这种模式适用于向后箭筒的, 使用这种模式时你应该保证只能在创建的线程中使用context,然而这不同意得到保证. 关于此模式的最新消息是iOS9中它将被废弃, 不推荐使用.
2.NSPrivateQueueConcurrencyType
在一个私有队列中创建并管理context.
3.NSMainQueueConcurrencyType
这种模式与第二种模式比较想死,只不过context与主队列绑定,因此也应该与evet loop紧密相连.当context与UI更新相关的话就是用这种模式
从iOS9以后就剩下 第二和第三 两种模式了, 那么搭建多线程CoreData环境的方案一般如下,创建一个NSManinQueueConcurrencyType的context用于相应UI时间,其他涉及大量数据操作可能会阻塞UI的,就是用NSPrivateQueueConcurrencyType的context
let mainContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
let backgroundContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)