1.線程安全的維度
1.線程間安全,不會產(chǎn)生線程死鎖、由線程操作產(chǎn)生的異常崩潰
2.線程數(shù)據(jù)安全,對于同時可讀可寫的數(shù)據(jù),需要有同步操作
2. 看一個多個線程同時讀寫操作一個變量的例子
@property (nonatomic,strong)NSString *target;
// queue 是并發(fā)對壘
for (int index = 0; index < 1000; index++) {
dispatch_async(queue, ^{
//如果是并行隊(duì)列里邊 改變同一塊內(nèi)存要加鎖
self.target = [NSString stringWithFormat:@"ksddkjalkjd%d",index];
NSLog(@"%@",self.target);
});
}
這段代碼存在的問題
1.target 打印的數(shù)據(jù)有肯能不是按順序的,也就是說數(shù)據(jù)會錯亂
2.程序崩潰. 原因:在iOS中 一個變量在獲取新值之前要釋放以前的舊址
在并行訪問同一塊資源的時候 如果釋放了一次舊址 再次釋放就會導(dǎo)致過度釋放
過度釋放會讓程序崩潰 報BAD_ACCESS_ADDRESS
這類問題我們需要用線程同步來處理,線程同步常用方法有:
- 互斥鎖和讀寫鎖:提供對臨界資源的保護(hù),當(dāng)多線程試圖訪問臨界資源時,都必須通過獲取鎖的方式來訪問臨界資源。(臨界資源:是被多線程共享的資源)當(dāng)讀寫線程獲取鎖的頻率差別不大時,一般采用互斥鎖,如果讀線程訪問臨界資源的頻率大于寫線程,這個時候采用讀寫鎖較為合適,讀寫鎖允許多個讀線程同時訪問臨界資源,讀寫線程必須互斥訪問臨界資源。讀寫鎖的實(shí)現(xiàn)采用了互斥鎖,所以在讀寫次數(shù)差不多的情況下采用讀寫鎖性能沒有直接采用互斥鎖來的高。
- 條件變量:提供線程之間的一種通知機(jī)制,當(dāng)某一條件滿足時,線程A可以通知阻塞在條件變量上的線程B,B所期望的條件已經(jīng)滿足,可以解除在條件變量上的阻塞操作,繼續(xù)做其他事情。
- 信號量:提供對臨界資源的安全分配。如果存在多份臨界資源,在多個線程爭搶臨界資源的情況下,向線程提供安全分配臨界資源的方法。如果臨界資源的數(shù)量為1,將退化為鎖。
- 令牌:一種高級的線程同步的方法。它既提供鎖的安全訪問臨界資源的功能,又利用了條件變量使得線程爭奪臨界資源時是有序的。
3.線程同步導(dǎo)致死鎖
例如: 1.線程循環(huán)等待導(dǎo)致死鎖
//當(dāng)前是主隊(duì)列
dispatch_sync(dispatch_get_main_queue(), ^{
});
// 如果是非主隊(duì)列也如此
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{
NSLog(@"外邊的1 %@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"里邊的 %@",[NSThread currentThread]);
});
NSLog(@"外邊的2 %@",[NSThread currentThread]);
});
2.線程同步時資源競爭
例如 下圖左右兩個線程要使用獨(dú)立的兩個資源A和B,而且只有在代碼塊結(jié)束后才讓別的線程使用A、B兩種資源,當(dāng)A和B分別被這兩個線程得到后,在執(zhí)行1和2兩步后,線程會死鎖,一直等待他們需要的資源線程會死鎖
解決方法
1.資源的加鎖范圍盡量縮小,也就是可以不占用后立馬釋放
2.加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,并釋放自己占有的鎖)
- 死鎖檢測是一個更好的死鎖預(yù)防機(jī)制,它主要是針對那些不可能實(shí)現(xiàn)按序加鎖并且鎖超時也不可行的場景。
