一 atomic
atomic
用于保證屬性setter、getter的原子性操作
寞埠,相當(dāng)于在getter
和setter
內(nèi)部加了線程同步的鎖
可以參考源碼objc4的objc-accessors.mm
- setter方法實(shí)現(xiàn)
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 = [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方法實(shí)現(xiàn)
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 = 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
內(nèi)部是線程同步
的
@property (copy, atomic) NSString *name;
- (void)setName:(NSString *)name {
// 加鎖
_name = name;
// 解鎖
}
- (NSString *)name {
// 加鎖
return _name;
// 解鎖
}
它并不能保證使用屬性的過(guò)程是線程安全的
@property (strong, atomic) NSMutableArray *data;
Person *p = [[Person alloc] init];
p.data = [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
(實(shí)際上調(diào)用了get
方法)和p.data = [NSMutableArray array];
(實(shí)際上調(diào)用了set
方法)是安全的恼五。但是多條線程同時(shí)添加對(duì)象時(shí)昌罩,即[p.data addObject:@"1"];
并不能保證線程安全。
二 讀與寫(xiě)的方案
思考如何實(shí)現(xiàn)以下場(chǎng)景
- 同一時(shí)間灾馒,只能有1個(gè)線程進(jìn)行寫(xiě)的操作
- 同一時(shí)間茎用,允許有多個(gè)線程進(jìn)行讀的操作
- 同一時(shí)間,不允許既有寫(xiě)的操作睬罗,又有讀的操作
上面的場(chǎng)景就是典型的“多讀單寫(xiě)”轨功,經(jīng)常用于文件等數(shù)據(jù)的讀寫(xiě)操作,iOS中的實(shí)現(xiàn)方案有
-
pthread_rwlock
:讀寫(xiě)鎖 -
dispatch_barrier_async
:異步柵欄調(diào)用
2.1 pthread_rwlock
等待鎖的線程會(huì)進(jìn)入休眠
代碼例子如下
#import <pthread.h>
@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);
}
執(zhí)行結(jié)果
2.2 dispatch_barrier_async
- 這個(gè)函數(shù)傳入的并發(fā)隊(duì)列必須是自己通過(guò)
dispatch_queue_cretate
創(chuàng)建的 - 如果傳入的是一個(gè)
串行
或是一個(gè)全局的并發(fā)隊(duì)列
容达,那這個(gè)函數(shù)便等同于dispatch_async
函數(shù)的效果
重要方法
// 初始化隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
// 讀
dispatch_async(self.queue, ^{
[self read];
});
// 寫(xiě)
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");
}
執(zhí)行結(jié)果如下
本文參考:
路飛_Luck (http://www.reibang.com/p/07f7b96bb03f)
以及借鑒MJ的教程視頻
非常感謝.