使用FMDB多线程訪问数据库,及database is locked的问题
今天最终攻克了多线程同一时候訪问数据库时,报数据库锁定的问题。错误信息是:
Unknown error finalizing or resetting statement (5: database is locked)
最后通过FMDatabaseQueue攻克了这个问题。本文总结一下:
FMDatabase不能多线程使用同一个实例
多线程訪问数据库,不能使用同一个FMDatabase的实例,否则会发生异常。假设线程使用单独的FMDatabase实例是同意的,可是相同有可能发生database is locked的问题。
这是因为多线程对sqlite的竞争引起的
我的app一開始就是多线程使用单独的FMDatabase实例訪问数据库,尽管没有引起crash。可是还是出现了database is locked问题,造成非常多数据没有如预期写入数据库
使用FMDatabaseQueue,问题依然
后来上FMDB的官网看了文档,确认用FMDatabaseQueue能够解决问题,API也比較简单:
NSString *dbFilePath = [PathResolver databaseFilePath]; queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath]; [queue inDatabase:^(FMDatabase *db){ // access db }];可是实际測试了一下,还是database is locked
读了一下相关的源代码。FMDatabaseQueue解决问题的思路是:创建一个队列。然后将放入队列的block顺序运行,这样避免了多线程同一时候訪问数据库
而我的代码是多线程各创建FMDatabaseQueue的实例,所以事实上有多个队列,因此还是存在数据库竞争的问题,和用FMDatabase时是一样的
共享同一个FMDatabaseQueue实例
于是接下来我让每一个线程使用同一个Queue实例。问题就顺利攻克了
实现的方式,一開始我想给FMDatabase添加一个单例方法。可是这样以后升级FMDB会比較麻烦。所以最后我是创建了一个Helper类
@implementation LosDatabaseHelper { FMDatabaseQueue* queue; } -(id) init { self = [super init]; if(self){ NSString *dbFilePath = [PathResolver databaseFilePath]; queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath]; } return self; } +(LosDatabaseHelper*) sharedInstance { static dispatch_once_t pred = 0; __strong static id _sharedObject = nil; dispatch_once(&pred, ^{ _sharedObject = [[self alloc] init]; }); return _sharedObject; } -(void) inDatabase:(void(^)(FMDatabase*))block { [queue inDatabase:^(FMDatabase *db){ block(db); }]; } @end
系统中其它的类。使用这个Helper类的单例,这样保证了全局仅仅有唯一的FMDatabaseQueue实例。
注意,由于Helper内部持有的是FMDatabaseQueue,所以能够这么做。假设包装的是FMDatabase类。就绝对会有问题。由于FMDatabase实例不能在多线程环境共享
使用FMDatabaseQueue之后。管理db
原本使用FMDatabase类,须要手工调用db的open和close方法
可是用FMDatabaseQueue,不须要调用open。由于查看代码发现,Queue已经open了。至于要不要close,我也不确定,由于官方的sample code没有调用close。实际应用中,我也没有调用。好像没有问题。假设须要close的话,我想能够在Helper类的公共方法里添加调用close queue就能够了。以下是close的源代码:
- (void)close { FMDBRetain(self); dispatch_sync(_queue, ^() { [_db close]; FMDBRelease(_db); _db = 0x00; }); FMDBRelease(self); }
所以。使用Queue,是不须要自己打开和关闭db的。可是假设使用了FMResultSet,rs倒是须要关闭,否则会报warning:
if ([db hasOpenResultSets]) { NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
为了不看到warning,我都在block里调用了[rs close]
刷新数据库文件路径
详细到我们的应用,另一个特殊问题须要考虑。
由于我们的APP能够切换账户,而账户的db文件是独立的。所以当用户又一次登录的时候,须要刷新一下Helper的queue
+(void) refreshDatabaseFile { LosDatabaseHelper *instance = [self sharedInstance]; [instance doRefresh]; } -(void) doRefresh { NSString *dbFilePath = [PathResolver databaseFilePath]; queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath]; }
假设不这么做,因为Helper是单例,那么切换账户以后。用户B訪问的还是用户A的数据库。刷新的调用。一般放在登录之后。进入主页面之前就能够了
队列和线程
在debug过程中,顺便看到一个现象。尽管多个block都是放到同一个队列里。可是事实上是跑在不同的thread里
不要混淆队列和线程的概念。使用GCD时。开发人员关注的是把block放到队列中,可是同一个队列事实上能够相应多个thread,为block分配thread,是GCD框架负责的,开发人员不须要关注。
仅仅要把操作放到合适的队列里,GCD就会完毕线程的创建,分配与回收
相关文章
- 数据上下文dbcontext添加数据与Database数据库的分库分表,表映射,切换表,使用到了IModelCacheKeyFactory代码如下:
- Myeclipse连接Mysql数据库时报错:Error while performing database login with the pro driver:unable
- Connect to Database Using Custom params链接数据库配置参数说明
- [AWS Design Cost-Optimized Architectures] 4.2 Identify cost-effective compute and database services
- [AWS] Create MySQL database in RDS and connect from local / EC2
- [Kotlin] Adding the Hibernate dependencies to our project and creating the database
- 数据库内核月报 - 2015 / 10-TokuDB · 捉虫动态 · CREATE DATABASE 导致crash问题
- CDH6.3.2添加Hue服务时,验证数据库连接报错 Unexpected error. Unable to verify database connection.
- ESF Database Migration Toolkit - 11.0.11
- 【报错】android.database.sqlite.SQLiteException: no such column: id (code 1): , while compiling: select
- Database之SQL:自定义创建数据库的各种表demo集合(以方便理解和分析sql的各种增删改查语法的具体用法)
- Database之SQL:SQL在线编程、工作中常用SQL代码实践之查询-SQL问题分析解决思路、高级案例SQL语法拆解(单技巧各自用法详细分类/多技巧组合用法)、经典组合案例实战之详细攻略
- Database之SQLSever:SQL命令实现四则运算、desc降序、like模糊查询、distinct去重、MAX/MIN/SUM/AVG/COUNT/GROUP/having等案例之详细攻略
- DBMS/Database:数据库管理的简介、安装(注意事项等)、学习路线(基于SQLSever深入理解SQL命令语句综合篇《初级→中级→高级》/几十项代码案例集合)之详细攻略
- Database之SQLSever:数据库管理人员国家职业资格证书中高级考试知识点(流式文件/封锁机制/三级模式(模式/内模式/外模式)/事务及其ACID特性/数据依赖/数据库的特点/安全/层次-网状
- Py之Database:Python和数据库的那些嘻嘻哈哈事详细攻略
- 【异常】dbeaver连接ClickHouse时:Unexpected driver error occurred while connecting to the database
- 假设web应用的文档根目录为MyApp,那么可以从哪里找到database.jar文件。
- Liquibase----XML格式通过update更新H2 Database数据库
- MySQL创建数据库(CREATE DATABASE语句)
- idea没有database选项卡
- MySQL数据库中出现no database selected是什么意思?
- 使用RMAN Convert Database命令实现跨平台的数据库迁移
- Oracle的学习心得和知识总结(十三)|Oracle数据库Real Application Testing之Database Replay实操(一)
- [ Azure - Database ] 解决办法:Incorrect date value ‘0000-00-00‘ for column ‘xxxx‘ at row 1