小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
我们在开发中有时会遇到这种问题,比如在内存中维护一份数据,有多处地方可能会同时操作这块数据,那么如何能保证这块数据的安全?如果要做到这种,需要满足以下三点条件:
- 1.读写互斥
- 2.写写互斥
- 3.读读互斥
@implementation KCPerson
- (instancetype)init{
if(self = [super init]){
_concurrentQueue = dispatch_queue_creat("com.kc_person.syncQueue", DISPATCH_QUEUE_CONCURRENT);
_dic = [NSMutableDictionary dictionary];
}
return self;
}
- (void)kc_setSafeObject:(id)object forKey:(NSString *)key{
key = [key copy];
dispatch_barrier_async(_concurrentQueue, ^{
[_dic setObject:object key:key];
});
}
- (id)kc_safeObjectForKey:(NSString *)key{
__block NSString *temp;
dispatch_sync(_concurrentQueue, ^{
temp = [_dic objcetForKey:key];
});
return temp;
}
@end
-
首先我们要维系一个GCD队列,最好不用全局队列,毕竟大家都知道全局队列遇到栅栏函数是有坑点的,这里就不分析了!(全局队列中,没有对栅栏函数的任何判断和处理。所以,栅栏函数在全局队列中,和普通的同步或者异步函数别无二致)
-
因为考虑性能、死锁、堵塞的因素不考虑穿行队列,用的是自定义的并发队列!
_concurrentQueue = dispatch_queue_creat("com.kc_person.syncQueue", DISPATCH_QUEUE_CONCURRENT);
-
首先我们来看看读操作:kc_safeObjectForKey我们考虑到多线程影响是不能用异步函数的!说明:
- 线程2获取:
name
线程3获取age
- 如果因为异步并发,导致混乱,本来读的是
name
,结果读到的是age
- 我们允许多个任务同时进去!但是读操作需要同步返回,所以我们选择:
同步函数
(读读并发)
- 线程2获取:
-
我们再来看看写操作,在写操作的时候对key进行了copy,关于此处的解释,插入一段来自参考文献的引用:
函数调用者可以自由传递一个
NSMutableString
的key
,并且能够在函数返回后修改它。因此我们必须对传入的字符串使用copy
操作以确保函数能够正常的工作。如果传入的字符串不是可变的(也就是正常的NSString
类型),调用copy
基本上是个空操作。
- 这里我们选择
dispatch_barrier_async
,为什么是栅栏函数而不是异步函数或者同步函数,下面分析:- 栅栏函数任务:之前所有的任务执行完毕,并且在它后面的任务开始之前,期间不会有其它的任务执行,这样比较好的促使写操作一个接一个写(写写互斥),不会乱!
- 为什么不是异步函数?异步函数会产生混乱
- 为什么不用同步函数?如果读写都操作了,那么用同步函数,就有可能存在:我写需要等待读操作回来才能执行,显然是不合理的!