iOS底层|iOS底层 -- 多线程之atomic、读写安全
一、atomic
atomic
用于保证属性setter、getter的原子性操作
,相当于在getter
和setter
内部加了线程同步的锁
可以参考源码objc4的objc-accessors.mm
- setter方法实现
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = https://www.it610.com/article/[newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}objc_release(oldValue);
}
- get方法实现
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = https://www.it610.com/article/objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
分析
nonatomic
和atomic
-
atom
:原子,不可再分割的单位 -
atomic
:原子性 - 给属性加上
atomic
修饰,可以保证属性
的setter
和getter
都是原子性操作
,也就是保证setter
和gette
内部是线程同步
的
@property (copy, atomic) NSString *name;
- (void)setName:(NSString *)name {
// 加锁
_name = name;
// 解锁
}- (NSString *)name {
// 加锁
return _name;
// 解锁
}
它并不能保证使用属性的过程是线程安全的
@property (strong, atomic) NSMutableArray *data;
Person *p = [[Person alloc] init];
p.data = https://www.it610.com/article/[NSMutableArray array];
// 以下操作就不能保证线程安全了
for (int i = 0;
i < 10;
i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[p.data addObject:@"1"];
});
}
虽然二 读与写的方案 思考如何实现以下场景data
属性是声明为atomic
,但是也只是在p.data
(实际上调用了get
方法)和p.data = https://www.it610.com/article/[NSMutableArray array];
(实际上调用了set
方法)是安全的。但是多条线程同时添加对象时,即[p.data addObject:@"1"];
并不能保证线程安全。
- 同一时间,只能有1个线程进行写的操作
- 同一时间,允许有多个线程进行读的操作
- 同一时间,不允许既有写的操作,又有读的操作
-
pthread_rwlock
:读写锁 -
dispatch_barrier_async
:异步栅栏调用
文章图片
代码例子如下
#import @property (assign, nonatomic) pthread_rwlock_t lock;
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
pthread_rwlock_init(&_lock, NULL);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0;
i < 10;
i++) {
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self write];
});
}
}- (void)read {
pthread_rwlock_rdlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}- (void)write {
pthread_rwlock_wrlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}- (void)dealloc {
pthread_rwlock_destroy(&_lock);
}
执行结果
2.2 dispatch_barrier_async
- 这个函数传入的并发队列必须是自己通过
dispatch_queue_cretate
创建的 - 如果传入的是一个
串行
或是一个全局的并发队列
,那这个函数便等同于dispatch_async
函数的效果
// 初始化队列
dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
// 读
dispatch_async(self.queue, ^{
[self read];
});
// 写
dispatch_barrier_async(self.queue, ^{
[self write];
});
原理如下
文章图片
代码例子如下
@property (strong, nonatomic) dispatch_queue_t queue;
// 栅栏
- (void)barrier_async_test {
self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0;
i < 5;
i++) {
dispatch_async(self.queue, ^{
[self read];
});
dispatch_async(self.queue, ^{
[self read];
});
dispatch_barrier_async(self.queue, ^{
[self write];
});
}
}- (void)read {
sleep(1);
NSLog(@"read");
}- (void)write {
sleep(1);
NSLog(@"write");
}
执行结果如下
文章图片
推荐阅读
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 爱就是希望你好好活着
- 昨夜小楼听风
- 知识
- 死结。
- 我从来不做坏事
- 2020-04-07vue中Axios的封装和API接口的管理
- 烦恼和幸福
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- iOS中的Block