51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

FMDB线程安全访问数据库

我们是使用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





赞(0)
未经允许不得转载:工具盒子 » FMDB线程安全访问数据库