FMDatabaseQueue 如何保证线程安全
这篇文章原来在用 Github Pages 搭建的博客上,现在决定重新用回博客园,所以把文章搬回来。
FMDB 是 OC 针对 sqlite 的封装。在其文档的线程安全部分这样讲:同时从多个线程使用同一个FMDatabase的实例是一个糟糕的想法。在单个线程中使用FMDatabase没有问题,但是不要在线程间共享一个FMDatabase的对象。如果你不听劝阻,那么坏的事情将接踵而至,比如应用崩溃或者异常,也有可能有陨石从天上掉下来砸向你的 Mac Pro(还好买不起垃圾桶)。这相当糟糕。
Using a single instance of FMDatabase from multiple threads at once is a bad idea. It has always been OK to make a FMDatabase object per thread. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. Bad things will eventually happen and you'll eventually get something to crash, or maybe get an exception, or maybe meteorites will fall out of the sky and hit your Mac Pro. This would suck.
同时,文档也给出了线程安全的方法,即使用FMDatabaseQueue。这篇文章就来分析 FMDatabaseQueue是如何做到线程安全的。
从初始化说起
+ (instancetype)databaseQueueWithPath:(NSString*)aPath;
+ (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags;
- (instancetype)initWithPath:(NSString*)aPath;
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags;
FMDatabaseQueue这四个初始化方法,其最终都会调用到initWithPath:flags:方法。其实现如下:
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags {
self = [super init];
if (self != nil) {
_db = [[[self class] databaseClass] databaseWithPath:aPath];
FMDBRetain(_db);
#if SQLITE_VERSION_NUMBER >= 3005000
BOOL success = [_db openWithFlags:openFlags];
#else
BOOL success = [_db open];
#endif
if (!success) {
NSLog(@"Could not create database queue for path %@", aPath);
FMDBRelease(self);
return 0x00;
}
_path = FMDBReturnRetained(aPath);
_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);
_openFlags = openFlags;
}
return self;
}
首先为实例化FMDatabase的一个实例_db,然后打开数据库。生成一个串行队列,然后调用dispatch_queue_set_specific为生成的 queue 设置关联的上下文数据。
这里需要注意两点,一是_db和_path都是FMDatabaseQueue的数据成员,二是为生成的串行队列关联的上下文数据是self,即FMDatabaseQueue本身。
inDatabase:
- (void)inDatabase:(void (^)(FMDatabase *db))block {
/* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue
* and then check it against self to make sure we're not about to deadlock. */
FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");
FMDBRetain(self);
dispatch_sync(_queue, ^() {
FMDatabase *db = [self database];
block(db);
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
#if defined(DEBUG) && DEBUG
NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]);
for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
NSLog(@"query: '%@'", [rs query]);
}
#endif
}
});
FMDBRelease(self);
}
开始的两行稍后讨论,我们往下看。
使用dispatch_sync同步派发到_queue,然后通过[self database]获取FMDatabase对象来执行对数据库的操作。自己可以看一下[self database]方法,实际上还是获取了初始化方法中的_db对象。这样就确保了唯一的FMDatabase对象在一个串行队列_queue中执行,而且是 sync。
FMDatabaseQueue中其他的方法,思路与此一致,不再讨论。
处理潜在的死锁问题
这里有一个问题,如果调用dispatch_sync的队列与其派发的队列是同一个队列,而且都是串行队列。那么会发生什么?没错,死锁。
我们在初始化函数中把FMDatabaseQueue的成员_queue初始化为了一个串行队列,那么如果调用inDatabase方法的队列跟_queue是同一个队列,就会造成死锁。开始的两行就是来处理这个问题。
当然,这发生的几率很小,一是初始化方法中dispatch_queue_create的第一个参数(用于指定队列的标签)是这样的[[NSString stringWithFormat:@"fmdb.%@", self] UTF8String],二是dispatch_queue_set_specific的key是这样的static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey;。如果你不故意要撞车,这个问题发生的概率要比中五百万的概率低得多。
FMDatabaseQueue 如何保证线程安全的更多相关文章
- EF 保证线程内唯一 上下文的创建
1.ef添加完这个对象,就会自动返回这个对象数据库的内容,比如下面这个表是自增ID 最后打印出来的ID 就是自增的结果 2.lambda 中怎么select * var userInfoList = ...
- 多线程下C#如何保证线程安全?
多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题.所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是 ...
- 在JAVA中ArrayList如何保证线程安全
[b]保证线程安全的三种方法:[/b]不要跨线程访问共享变量使共享变量是final类型的将共享变量的操作加上同步一开始就将类设计成线程安全的, 比在后期重新修复它,更容易.编写多线程程序, 首先保证它 ...
- java中volatile不能保证线程安全
今天打了打代码研究了一下java的volatile关键字到底能不能保证线程安全,经过实践,volatile是不能保证线程安全的,它只是保证了数据的可见性,不会再缓存,每个线程都是从主存中读到的数据,而 ...
- 最近面试被问到一个问题,AtomicInteger如何保证线程安全?
最近面试被问到一个问题,AtomicInteger如何保证线程安全?我查阅了资料 发现还可以引申到 乐观锁/悲观锁的概念,觉得值得一记. 众所周知,JDK提供了AtomicInteger保证对数字的操 ...
- OSSpinLockLock加锁机制,保证线程安全并且性能高
在aspect_add.aspect_remove方法里面用了aspect_performLocked, 而aspect_performLocked方法用了OSSpinLockLock加锁机制,保证线 ...
- Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S
Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...
- (C#) 多线程访问探讨,如果保证线程安全?
先抛出几点疑问: 1. 多个线程同时访问同一个“值类型变量“(value type, stored in stack), 如果保证安全访问? 2. 多个线程同时访问同一个“引用类型变量“(refere ...
- ConcurrentHashMap如何保证线程安全
以前看过HashMap的内部实现,知道HashMap是使用Node数组+链表+红黑树的数据结构来实现,如下图所示.但是HashMap是非线程安全,在多线程环境不能够使用. 不过JDK在其并发包中为我们 ...
随机推荐
- 常用的CSS框架
常用的CSS框架 之前在写自己的个人网站的时候,由于自己Web前端不是特别好,于是就去找相关的CSS框架来搭建页面了. 找到以下这么一篇文章(列出了很多常用的CSS框架): http://w3scho ...
- 试着简单易懂记录synchronized this object Class的区别,模拟ConcurrentHashMap
修饰静态方法默认获取当前class的锁,同步方法没有释放的锁,不影响class其他非同步方法的调用,也不影响不同锁的同步方法,更不影响使用class的其他属性. package thread.base ...
- HADOOP集群配置
http://wenku.baidu.com/view/92cbe435eefdc8d376ee32eb.html http://www.infoq.com/cn/articles/hadoop-co ...
- CSS盒模型的深度思考及BFC
本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. 题目:谈一谈你对CSS盒模型的认识 专业的面试,一定会问 CSS 盒模型 ...
- R语言︱list用法、批量读取、写出数据时的用法
列表是一种特别的对象集合,它的元素也由序号(下标)区分,但是各元素的类型可 以是任意对象,不同元素不必是同一类型.元素本身允许是其它复杂数据类型,比如,列表 的一个元素也允许是列表.例如: > ...
- org.apache.jasper.JasperException
1.错误描述 2014-7-13 17:20:50 org.apache.catalina.core.StandardWrapperValve invoke 严重: Servlet.service() ...
- Ajax跨域解决实例
HTML 在本例中我们跨域http://www.sucaihuo.com/jquery/demo/43/jsonp.php?callback=.你也可以试试其他域名.下面表单中,我们提交后的结果反馈到 ...
- iOS - UIImageView 动画
1.UIImageView 动画 1.1 播放图片集 播放图片集 @property (nonatomic, strong) UIImageView *playImageView; self.play ...
- eval及json的理解
一,案例一 <br><br> 复制代码 var str = '{"name": "hanzichi", "age": ...
- Windows Developer Day - Windows AI Platform
本次 Windows Developer Day,最值得期待的莫过于 Windows AI Platform 了,可以说是千呼万唤始出来.观看直播的开发者们,留言最多的也是 Windows AI Pl ...