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

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. Python Importlib模块与__import__详解

    Importlib模块与__import__都可以通过过字符串来导入另外一个模块,但在用法上和本质上都有很大的不同. 以一个例子为证: 以下为我的工程目录结构: lib/test.py: name = ...

  2. Django中重定向页面的时候使用命名空间

    urls.py from django.urls import path from . import views app_name='front' urlpatterns = [ path('',vi ...

  3. [android开发篇]elicpse安装教程

    http://www.runoob.com/eclipse/eclipse-install.html Eclipse 安装(Neon 版本) Eclipse 最新版本 Eclipse Neon,这个首 ...

  4. 长沙理工大学第十二届ACM大赛-重现赛

    年轮广场 时间限制:1秒 空间限制:131072K 题目描述 在云塘校区,有一个很适合晒太阳的地方————年轮广场 年轮广场可以看成n个位置顺时针围成一个环. 这天,天气非常好,Mathon带着他的小 ...

  5. iOS转场动画初探

    一般我们就用两种转场push和present present /** 1.设置代理 - (instancetype)init { self = [super init]; if (self) { se ...

  6. 【Luogu】P2015二叉苹果树(DP,DFS)

    题目链接 设f[i][j][k]表示给以i为根节点的子树分配j条可保留的树枝名额的时候,状态为k时能保留的最多苹果. k有三种情况. k=1:我只考虑子树的左叉,不考虑子树的右叉,此时子树能保留的最多 ...

  7. 算法复习——拓展欧几里得(poj1061)

    题目: Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很 ...

  8. 常州模拟赛d6t3 噪音

    FJ有M个牛棚,编号1至M,刚开始所有牛棚都是空的.FJ有N头牛,编号1至N,这N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚.第i头奶牛选择走进第p[i]个牛棚.由于奶牛是群体动 ...

  9. 【kmp+最小循环节】poj 2406 Power Strings

    http://poj.org/problem?id=2406 [题意] 给定字符串s,s=a^n,a是s的子串,求n最大是多少 [思路] kmp中的next数组求最小循环节的应用 例如 ababab ...

  10. 如何将一个int转换成cstring

    如:int a = 5;CString b;b.Format("%d",a);补充:如果a是double,或a是float的就是:b.Format("%f",a ...