使用FMDB多线程访问数据库,及database is locked的问题

简介: 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 今天终于解决了多线程同时访问数据库时,报数据库锁定的问题,错误信息是: Unknown error finalizing or resetting statement (5: database is locked) 最后通过FMDatabaseQueue解决了这个问题,本文总结一下: FMDatabase不能多线程使用同一个实例 多线程访问数据库,不能使用同一个FMDatabase的实例,否则会发生异常。

每日更新关注:http://weibo.com/hanjunqiang  新浪微博

今天终于解决了多线程同时访问数据库时,报数据库锁定的问题,错误信息是:

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
}];
每日更新关注 : http://weibo.com/hanjunqiang   新浪微博


但是实际测试了一下,还是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

每日更新关注:http://weibo.com/hanjunqiang  新浪微博

系统中其他的类,使用这个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就会完成线程的创建,分配与回收

每日更新关注:http://weibo.com/hanjunqiang  新浪微博

目录
相关文章
|
1月前
|
并行计算 安全 程序员
【C++】—— C++11之线程库
【C++】—— C++11之线程库
|
3月前
|
SQL NoSQL 关系型数据库
二、什么是数据库(DataBase)
二、什么是数据库(DataBase)
69 0
|
1月前
|
Java 调度 Python
深入解析 Python asyncio 库:如何使用线程池实现高效异步编程
深入解析 Python asyncio 库:如何使用线程池实现高效异步编程
41 0
|
5月前
|
编译器 Linux 调度
|
2月前
|
存储 关系型数据库 数据库
在进行RDS(Amazon Relational Database Service,亚马逊关系数据库服务)迁移时,兼容性审查
在进行RDS(Amazon Relational Database Service,亚马逊关系数据库服务)迁移时,兼容性审查
19 1
|
2月前
|
SQL 存储 关系型数据库
MySQL技能完整学习列表——1、数据库基础概念——1、关系型数据库(Relational Database)
MySQL技能完整学习列表——1、数据库基础概念——1、关系型数据库(Relational Database)
172 0
|
3月前
|
数据可视化 关系型数据库 Java
数据库导出神器:Database-Export
Database-Export是一款开源的数据库导出工具
162 0
数据库导出神器:Database-Export
|
8月前
|
存储 设计模式 Java
4.7 C++ Boost 多线程并发库
C++语言并没有对多线程与网络的良好支持,虽然新的C++标准加入了基本的`thread`库,但是对于并发编程的支持仍然很基础,Boost库提供了数个用于实现高并发与网络相关的开发库这让我们在开发跨平台并发网络应用时能够像Java等语言一样高效开发。 thread库为C++增加了多线程处理能力,其主要提供了清晰的,互斥量,线程,条件变量等,可以很容易的实现多线程应用开发,而且该库是可跨平台的,并且支持`POSIX`和`Windows`线程。
102 0
|
4月前
|
安全 Linux 编译器
C++11『lambda表达式 ‖ 线程库 ‖ 包装器』
C++11『lambda表达式 ‖ 线程库 ‖ 包装器』
37 0
|
4月前
|
存储 机器学习/深度学习 关系型数据库
向量数据库(vector database)概念初认知
向量可以进行比较,如果将那些不可以比较的数据转换成为向量,且这些向量的分布和距离可以反映出实体的关系,那么就可以通过比较向量的关键进行检索,也就可以实现图搜图、文搜文的功能了。

热门文章

最新文章