今天最终攻克了多线程同一时候訪问数据库时,报数据库锁定的问题。错误信息是:

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就会完毕线程的创建,分配与回收

使用FMDB多线程訪问数据库,及database is locked的问题相关推荐

  1. 多线程訪问共享数据(1)

    多线程訪问共享数据解决方式: 一.什么是多线程 线程是程序中一个单一的顺序控制流程.在单个程序中同一时候运行多个线程完毕不同的工作,称为多线程. 全部的线程尽管在微观上是串行运行的,可是在宏观上你全然 ...

  2. 使用c#訪问Access数据库时,提示找不到可安装的 ISAM

    使用c#訪问Access数据库时,提示找不到可安装的 ISAM.例如以下图: 代码例如以下: connectionString = "Provider=Microsoft.Jet.OLEDB ...

  3. 机房重构 之 抽象工厂+反射+配置文件 实现数据库訪问

    重构机房已经開始三个多星期了,从刚開始的一头雾水,到如今的柳暗花明,由開始的无从下手,到如今感觉犹 如脱胎换骨了般.和两个星期前相比.如今明朗了多了,心情也好了不少. 先给大家看一下这次重构的总体架构 ...

  4. Windows server2008 搭建ASP接口訪问连接oracle数据库全过程记录

    真的是太不easy了,曾经的时候在window server 2003上面搭建了一套asp+oracle的接口系统.就费了好大的劲儿,事实上那会迷迷瞪瞪的也不知道怎么的就弄好了,也懒得管了.OK,从昨 ...

  5. FMDB数据库损坏 database disk image is malformed, code:11

    FMDB数据库损坏 database disk image is malformed, code:11 使用FMDB数据库的时候,有时会遇到 数据库损坏的问题,FMDB又没有提供修复工具,导致App数 ...

  6. c#数据库訪问返回值类型为SqlDataReader时使用using时注意的问题

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u010512579/article/details/24011761 在封装通用 SQLSERVER ...

  7. HDFS简单介绍及用C语言訪问HDFS接口操作实践

    一.概述 近年来,大数据技术如火如荼,怎样存储海量数据也成了当今的热点和难点问题,而HDFS分布式文件系统作为Hadoop项目的分布式存储基础,也为HBASE提供数据持久化功能,它在大数据项目中有很广 ...

  8. Phalcon 訪问控制列表 ACL(Access Control Lists ACL)

    Phalcon在权限方面通过 Phalcon\Acl 提供了一个轻量级的 ACL(訪问控制列表). Access Control Lists (ACL) 同意系统对用户的訪问权限进行控制,比方同意訪问 ...

  9. golang sqlite数据库 rows.Close()造成 错误database is locked

    在最近的一个程序中,使用的是sqlite数据库.涉及到多线程对数据库的读写.因为sqlite本身有五个锁状态:unlocked,shared,reserved,pending,exclusive.每个 ...

最新文章

  1. 总结vue中父向子,子向父以及兄弟之间通信的几种方式
  2. gdb加载python_gdb加载python脚本的方法
  3. c语言猴子吃桃嵌套调用编程,C语言实现猴子吃桃问题(循环、递归两种方法)...
  4. Arduino--蓝牙
  5. SQL中的long text
  6. Spring中任务调度cronExpression配置说明
  7. JAVA的extends用法
  8. arrylist和linked list区别
  9. javascript学习系列(19):数组中的Array.from方法
  10. 最悲剧的HTML5 API : Position地理位置
  11. r语言 读取dta_R语言与计量经济学(三)异方差
  12. mysql-笔记-命名、索引规范
  13. Python语言学习 (六)1.2
  14. 使用Maven构建Spring项目“HelloWorld”
  15. JavaScript——exec和match
  16. 【单目标优化求解 】基于matlab烟花算法求解单目标问题【含Matlab源码 1599期】
  17. Android源码在线查看工具
  18. python 公众号开发框架_基于werobot框架的微信公众号开发
  19. pattern.compile java_Java Pattern compile(String)用法及代码示例
  20. Android 屏蔽返回键、菜单键和Home键

热门文章

  1. 深入分析几种PHP获取客户端IP的情况
  2. php smarty 源码,Smarty php源码 v3.1.39
  3. 发展农业对话国际农民丰收节贸易会 菲律宾学中国还是印度?
  4. Singleton单例模式
  5. Solr安装并导入mysql数据
  6. @hdu - 3746@ Cyclic Nacklace
  7. Vue.js的复用组件开发流程
  8. 精通移动端布局 - 概念篇 -
  9. iOS开发之功能模块--推送之坑问题解决
  10. OpenMeetings的安装