日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

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

發(fā)布時(shí)間:2025/3/20 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用FMDB多线程訪问数据库,及database is locked的问题 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

今天最終攻克了多線程同一時(shí)候訪問數(shù)據(jù)庫時(shí),報(bào)數(shù)據(jù)庫鎖定的問題。錯(cuò)誤信息是:

Unknown error finalizing or resetting statement (5: database is locked)

最后通過FMDatabaseQueue攻克了這個(gè)問題。本文總結(jié)一下:

FMDatabase不能多線程使用同一個(gè)實(shí)例

多線程訪問數(shù)據(jù)庫,不能使用同一個(gè)FMDatabase的實(shí)例,否則會(huì)發(fā)生異常。假設(shè)線程使用單獨(dú)的FMDatabase實(shí)例是同意的,可是相同有可能發(fā)生database is locked的問題。

這是因?yàn)槎嗑€程對(duì)sqlite的競(jìng)爭(zhēng)引起的

我的app一開始就是多線程使用單獨(dú)的FMDatabase實(shí)例訪問數(shù)據(jù)庫,盡管沒有引起crash。可是還是出現(xiàn)了database is locked問題,造成非常多數(shù)據(jù)沒有如預(yù)期寫入數(shù)據(jù)庫

使用FMDatabaseQueue,問題依然

后來上FMDB的官網(wǎng)看了文檔,確認(rèn)用FMDatabaseQueue能夠解決問題,API也比較簡(jiǎn)單:

NSString *dbFilePath = [PathResolver databaseFilePath]; queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath]; [queue inDatabase:^(FMDatabase *db){// access db }];可是實(shí)際測(cè)試了一下,還是database is locked

讀了一下相關(guān)的源代碼。FMDatabaseQueue解決問題的思路是:創(chuàng)建一個(gè)隊(duì)列。然后將放入隊(duì)列的block順序運(yùn)行,這樣避免了多線程同一時(shí)候訪問數(shù)據(jù)庫

而我的代碼是多線程各創(chuàng)建FMDatabaseQueue的實(shí)例,所以事實(shí)上有多個(gè)隊(duì)列,因此還是存在數(shù)據(jù)庫競(jìng)爭(zhēng)的問題,和用FMDatabase時(shí)是一樣的

共享同一個(gè)FMDatabaseQueue實(shí)例

于是接下來我讓每一個(gè)線程使用同一個(gè)Queue實(shí)例。問題就順利攻克了

實(shí)現(xiàn)的方式,一開始我想給FMDatabase添加一個(gè)單例方法。可是這樣以后升級(jí)FMDB會(huì)比較麻煩。所以最后我是創(chuàng)建了一個(gè)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
系統(tǒng)中其它的類。使用這個(gè)Helper類的單例,這樣保證了全局僅僅有唯一的FMDatabaseQueue實(shí)例。

注意,由于Helper內(nèi)部持有的是FMDatabaseQueue,所以能夠這么做。假設(shè)包裝的是FMDatabase類。就絕對(duì)會(huì)有問題。由于FMDatabase實(shí)例不能在多線程環(huán)境共享

使用FMDatabaseQueue之后。管理db

原本使用FMDatabase類,須要手工調(diào)用db的open和close方法

可是用FMDatabaseQueue,不須要調(diào)用open。由于查看代碼發(fā)現(xiàn),Queue已經(jīng)open了。至于要不要close,我也不確定,由于官方的sample code沒有調(diào)用close。實(shí)際應(yīng)用中,我也沒有調(diào)用。好像沒有問題。假設(shè)須要close的話,我想能夠在Helper類的公共方法里添加調(diào)用close queue就能夠了。以下是close的源代碼:

- (void)close {FMDBRetain(self);dispatch_sync(_queue, ^() { [_db close];FMDBRelease(_db);_db = 0x00;});FMDBRelease(self); }
所以。使用Queue,是不須要自己打開和關(guān)閉db的。可是假設(shè)使用了FMResultSet,rs倒是須要關(guān)閉,否則會(huì)報(bào)warning:

if ([db hasOpenResultSets]) {NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
為了不看到warning,我都在block里調(diào)用了[rs close]

刷新數(shù)據(jù)庫文件路徑

詳細(xì)到我們的應(yīng)用,另一個(gè)特殊問題須要考慮。

由于我們的APP能夠切換賬戶,而賬戶的db文件是獨(dú)立的。所以當(dāng)用戶又一次登錄的時(shí)候,須要刷新一下Helper的queue

+(void) refreshDatabaseFile {LosDatabaseHelper *instance = [self sharedInstance];[instance doRefresh]; }-(void) doRefresh {NSString *dbFilePath = [PathResolver databaseFilePath];queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath]; }
假設(shè)不這么做,因?yàn)镠elper是單例,那么切換賬戶以后。用戶B訪問的還是用戶A的數(shù)據(jù)庫。刷新的調(diào)用。一般放在登錄之后。進(jìn)入主頁面之前就能夠了

隊(duì)列和線程

在debug過程中,順便看到一個(gè)現(xiàn)象。盡管多個(gè)block都是放到同一個(gè)隊(duì)列里。可是事實(shí)上是跑在不同的thread里

不要混淆隊(duì)列和線程的概念。使用GCD時(shí)。開發(fā)人員關(guān)注的是把block放到隊(duì)列中,可是同一個(gè)隊(duì)列事實(shí)上能夠相應(yīng)多個(gè)thread,為block分配thread,是GCD框架負(fù)責(zé)的,開發(fā)人員不須要關(guān)注。

僅僅要把操作放到合適的隊(duì)列里,GCD就會(huì)完畢線程的創(chuàng)建,分配與回收


總結(jié)

以上是生活随笔為你收集整理的使用FMDB多线程訪问数据库,及database is locked的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。