Objective-c 線程系列一 atomic是安全的嗎
Objective-c 線程系列二 @synchronized
Objective-c 線程系列三 NSRecursiveLock
一 Property聲明是atomic
1 簡單的將property分為值類型和對象類型,值類型是指primitive type,包括int,long,bool等非對象類型,另一種是對象類型,聲明為指針,可以指向某個符合類型定義的內(nèi)存區(qū)域。
2 討論多線程安全的時候,其實是在討論多個線程同時訪問一個內(nèi)存區(qū)域的安全問題。針對同一塊區(qū)域,有讀和寫的兩種操作,讀和寫同時發(fā)生在同一塊區(qū)域的時候,就有可能出現(xiàn)多線程不安全。
3 我們只有一個地址總線,一個內(nèi)存。即使在多線程的環(huán)境下,也不可能存在兩個線程同時訪問同一塊內(nèi)存區(qū)域的場景,內(nèi)存的訪問一定是通過一個地址總線串行排隊訪問的。所以,內(nèi)存的訪問是串行的,并不會導(dǎo)致內(nèi)存數(shù)據(jù)的錯亂或者應(yīng)用的crash。
4 如果讀寫的內(nèi)存長度小于等于地址總線的長度,那么讀寫的操作是原子的,一次完成。比如bool,int,long在64位操作系統(tǒng)下的單次讀寫都是原子操作。所以,從訪問內(nèi)存的角度看nonatomic和atomic沒有區(qū)別。
二 atomic的作用到底是什么?
生成原子操作的getter和setter
設(shè)置atomic之后,默認生成的getter和setter方法執(zhí)行時是原子的。也就是說,當我們在線程A執(zhí)行g(shù)etter方法的時候(創(chuàng)建調(diào)用棧,返回地址,出棧),線程B如果想執(zhí)行setter方法,必須先等getter方法完成才能執(zhí)行。舉個例子,在32位系統(tǒng)里,如果通過getter返回64位的double,地址總線寬度為32位,從內(nèi)存當中讀取double的時候無法通過原子操作完成,如果不通過atomic加鎖,有可能會在讀取的中途在其他線程發(fā)生setter操作,從而出現(xiàn)異常值。如果出現(xiàn)這種異常值,就發(fā)生了多線程不安全。
三 是不是使用了atomic就一定多線程安全呢?
不一定
atomic 只是給getter和setter加了個鎖,atomic只能保證代碼進入getter或者setter函數(shù)內(nèi)部是安全的,一旦出了getter和setter,多線程的安全只能靠程序員自己保障了。atomic由于加鎖也會帶來一些性能損耗,所以我們在編寫iOS代碼的時候,一般聲明property為nonatomic,自己去加鎖做同步。
四 使用了atomic線程不安全的例子
@property (atomic,strong) NSArray *array;
- (void)testAtomic{
dispatch_queue_t oneQueue = dispatch_queue_create("11", DISPATCH_QUEUE_CONCURRENT);;
dispatch_async(oneQueue, ^{
for (int i = 0; i < 1000000; i++) {
if (i%2 == 0) {
self.array = @[@"3",@"3",@"444"];
}else{
self.array = @[@"4"];
}
}
});
dispatch_queue_t secondQueue = dispatch_queue_create("22", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(secondQueue, ^{
for (int i = 0; i < 1000000; i++) {
if (self.array.count > 2) {
NSNumber *str = [self.array objectAtIndex:1];
NSLog(@"str %@",str);
}
}
});
}
五 如何做到多線程安全問題
做到代碼串行的執(zhí)行,能保證代碼執(zhí)行到一半的時候,不會有另一個線程介入,就能做到多線程安全問題。這時我們可以使用加鎖的方式,來保證代碼的串行。
在做多線程安全的時候,并不是通過property加atomic關(guān)鍵字來保證安全,而是將property聲明為nonatomic(nonatomic沒有g(shù)etter,setter的鎖開銷),然后自己加鎖。
參考文檔
蘋果開發(fā)者文檔
細說@synchronized和dispatch_once
正確使用多線程同步鎖@synchronized()
runtime源碼
objc-sync.mm源碼