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

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. VC++ 訪问数据库实例具体解释图解

    一 ADO 方式訪问 Access 新建一个对话框project,加入控件,如图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2 ...

  2. 假设在本地搭一个server和mysql数据库环境,假设使用java来訪问数据库

    我们能够使用speedamp来搭一个server环境,能够在http://download.csdn.net/detail/baidu_nod/7630265下载 解压后无需安装直接能够使用.点击Sp ...

  3. mysql设置远程訪问数据库的多种方法

    问题:MySQL权限设置正确,但仍无法远程訪问.通过telnet发现3306port未打开. 分析:MySQL默认仅仅绑定127.0.0.1,即:仅仅有在本机才干訪问3306port. 解决:找到My ...

  4. linux c编程訪问数据库

    源代码例如以下: #include <stdio.h> #include <stdlib.h> #include <mysql/mysql.h> int main( ...

  5. 三国武将查询系统 //Java 訪问 数据库

    import java.awt.*; import javax.swing.*; import java.awt.event.ActionListener; import java.awt.event ...

  6. 利用httpclient和多线程刷訪问量代码

    缘起于玩唱吧,由于唱吧好友少,訪问量低,又不想加什么亲友团之类的,主要是太麻烦了,于是我就琢磨唱吧的訪问机制,准备用java的httpclient库来进行刷訪问量,想到动态IP反复使用就想到了用多线程 ...

  7. 学习实践:使用模式,原则实现一个C++数据库訪问类

    一.概述 在我參与的多个项目中.大家使用libMySQL操作MySQL数据库,并且是源代码级复用,在多个项目中同样或相似的源代码.这种复用方式给开发带来了不便. libMySQL的使用比較麻烦.非常e ...

  8. 使用ADO.NET对SQL Server数据库进行訪问

    在上一篇博客中我们给大家简介了一下VB.NET语言的一些情况,至于理论知识的学习我们能够利用VB的知识体系为基础.再将面向对象程序设计语言的知识进行融合便可进行编程实战. 假设我们须要訪问一个企业关系 ...

  9. Java数据库訪问小结

    </pre>1.JDBC訪问方法</p><p></p><p>DBHelper类訪问数据库.Dao类写数据訪问,View类进行应用,初学实例图 ...

随机推荐

  1. 异步FIFO最小深度计算

    计算FIFO深度是设计FIFO中常遇到的问题.常识告诉我们,当读速率慢于写速率时,FIFO便可被用作系统中的缓冲元件或队列.因此FIFO的大小基本上暗示了所需缓存数据的容量,该容量取决于读写数据的速率 ...

  2. Cplex: MIP Callback Interface

    *本文主要记录和分享学习到的知识,算不上原创 *参考文献见链接 这篇文章主要记录一些Cplex的Callback的使用方法,采用Java语言. https://www.ibm.com/support/ ...

  3. GT使用说明

    GT文档:https://gt.qq.com/docs.html GT Android版的详细使用手册: https://gt.qq.com/docs/a/GTAndroidUserGuide.pdf

  4. IS-IS IGP

    is-is  是igp的一种    属于osi的协议 OSI的三层是网络层      包含两种服务  一种是面向连接服务CONS       另一种是无连接服务CLNS CLNS中包含CLNP     ...

  5. Laya Timer原理 & 源码解析

    Laya Timer原理 & 源码解析 @author ixenos 2019-03-18 16:26:38 一.原理 1.将所有Handler注册到池中 1.普通Handler在handle ...

  6. TOJ 3134: 渊子赛马修改版

    3134: 渊子赛马修改版 Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByteTotal Submit: 458     ...

  7. NYOJ-525一道水题思路及详解

    一道水题 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描述 今天LZQ在玩一种小游戏,但是这游戏数有一点点的大,他一个人玩的累,想多拉一些人进来帮帮他,你能写一个程序帮帮他 ...

  8. 【Luogu】P1144最短路计数(BFS)

    题目链接 此题使用BFS记录最短路的条数.思路如下:因为是无权无向图,所以只要被BFS到就是最短路径.因此可以记录该点的最短路和最短路的条数:如果点y还没被访问过,则记录dis[y],同时令ans[y ...

  9. 洛谷P3760 - [TJOI2017]异或和

    Portal Description 给出一个\(n(n\leq10^5)\)的序列\(\{a_n\}(\Sigma a_i\leq10^6)\),求该数列所有连续和的异或和. Solution 线段 ...

  10. java 汉字保存到mysql 乱码

    保存之前正常,插入数据乱码 确认jsp mysql编码都确定为utf8 在连接数据库是加上编码 jdbc:mysql://localhost:3306/test?useUnicode=true& ...