我们是使用FMDB保存好友,联系人数据,在开始使用FMDB的进行小批量数据的读写时,开始还是蛮正常的,随着数据量以及业务的复杂增加,发现了一些离奇的问题:
1、偶现联系人数据表中存在重复记录;
2、偶现读取不到数据,但拉数据库里面却有数据;
根据业务场景分析,确实存在并发读写的情况,由于我们使用的是单例模式,所以问题1在不进行多线程互斥访问的情况下,确实是存在这个问题,所以想到的思路是将所有读写操作都放到一个队列中,执行完成了在通知UI获取数据,这个想法竟然和FMDatabaseQueue的思路是一样的,但网上说FMDatabaseQueue还是存在线程安全的问题,有点庆幸没有用这个方案解决多线程并发读写的问题!
一种是多实例多线程模式,一种是单线程模式, 这个在使用多线程模式下也存在多线程访问安全的问题,所以使用了网上下面的配置:
sqlite3_open_v2(path, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, nil)
DBWrapper的封装:
@interface DBWraper () //用于对所有sql操作互斥 @property(nonatomic, strong) NSRecursiveLock *lock; @end @implementation DBWraper +(instancetype) getInstance{ static DBWraper *_shareSingleton=nil; static dispatch_once_t onceTokens; dispatch_once(&onceTokens,^{ _shareSingleton=[[self alloc]init]; }); return _shareSingleton; } - (id)init{ self = [super init]; if (self){ self.lock = [[NSRecursiveLock alloc] init]; } return self; } - (BOOL)lockCurrentThread:(NSThread *)currentThread currentFunc:(NSString*) currentFunc{ BOOL status = [self.lock tryLock]; if (!status){ LOG_ERROR(TAG_DB_MODULE, @"lockCurrentThread try lock failed! Lock thread:[%@] %@", currentThread, currentFunc); if ([NSThread isMainThread]){ //lock失败情况下,直接放行,但不一定保证能读取到数据 return NO; } LOG_ERROR(TAG_DB_MODULE, @"lockCurrentThread not main thread, continue wait."); //wait here [self.lock lock]; } return YES; } - (void)unlockCurrentThread{ [self.lock unlock]; if (DEBUG){ LOG_INFO(TAG_DB_MODULE, @"unlockCurrentThread success!"); } } +(void) dispatch_db_task:(dispatch_block_t) block{ dispatch_group_async([DBWraper getDBGroup], [DBWraper getDBQueue], block); }; +(void) dispatch_db_notify_main:(dispatch_block_t) block{ dispatch_group_notify([DBWraper getDBGroup],dispatch_get_main_queue(),block); } +(dispatch_queue_t) getDBQueue{ static dispatch_queue_t dbQueue = nil; static dispatch_once_t onceTokens; dispatch_once(&onceTokens,^{ dbQueue = dispatch_queue_create( "db_update_queue", DISPATCH_QUEUE_SERIAL);//DISPATCH_QUEUE_CONCURRENT }); return dbQueue; } +(dispatch_group_t) getDBGroup{ static dispatch_group_t group = nil; static dispatch_once_t onceTokens; dispatch_once(&onceTokens,^{ group = dispatch_group_create(); }); return group; } @end
业务模块:
[ DBWraper dispatch_db_task:^{ LOCK_DB_OPERATION }]; [DBWraper dispatch_db_notify_main:^{ //通知更新UI }];
使用LockGuard进行递归互斥锁保护单例对象的互斥方法:
//只需要new这个对象就可以保证如下操作被加锁,函数退出后自动解锁该对象
#define LOCK_DB_OPERATION LockGuard *lockGuard = [LockGuard new];
//用于封装线程递归互斥锁对象 @interface LockGuard:NSObject @end @interface LockGuard () @property(nonatomic, strong) NSString *lastCurrentlockFunc; @property(nonatomic, strong) NSThread *lastCurrentlockThread; @property(nonatomic, strong) NSString *currentlockFunc; @property(nonatomic, strong) NSThread *currentlockThread; @end @implementation LockGuard - (id)init{ self = [super init]; if (self){ NSArray *syms = [NSThread callStackSymbols]; if ([syms count] > 1) { self.lastCurrentlockFunc = self.currentlockFunc; self.lastCurrentlockThread = self.currentlockThread; self.currentlockFunc = [syms objectAtIndex:1]; self.currentlockThread = [NSThread currentThread]; } if (DEBUG){ LOG_INFO(TAG_DB_MODULE, @"lock - caller:%@ ", self.currentlockFunc); } BOOL value = [[DBWraper getInstance] lockCurrentThread:self.currentlockThread currentFunc:self.currentlockFunc]; if (!value){ LOG_ERROR(TAG_DB_MODULE, @"lock failed -last caller:%@,thread:%@ ", self.currentlockFunc, self.lastCurrentlockThread); } } return self; } - (void)dealloc{ [[DBWraper getInstance] unlockCurrentThread]; if (DEBUG){ NSArray *syms = [NSThread callStackSymbols]; if ([syms count] > 1) { LOG_INFO(TAG_DB_MODULE, @"unlock - caller:%@ ", self.currentlockFunc); } } } @end
图片来自:https://blog.csdn.net/a18339063397/article/details/90719433
-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com
本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com