資源共享
1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源
好比多個線程訪問同一個對象、同一個變量、同一個文件
當多個線程訪問同一塊資源時,很容易引起
數據錯亂和數據安全問題
1、解決方案
解決方案:使用線程同步技術(同步,就是協同步調,按預約的前後次序進行)
常見的線程同步技術是:加鎖
一、OSSpinLock
OSSpinLock叫作」自旋鎖」,等待鎖的線程會處於忙等(busy-wait)狀態,一直佔用着CPU資源
目前已經再也不安全,可能會出現優先級反轉問題
若是等待鎖的線程優先級較高,它會一直佔用着CPU資源,優先級低的線程就沒法釋放鎖
須要導入頭文件#import<libkern/OSAtomic.h>
二、os_unfair_lock
os_unfair_lock用於取代不安全的OSSpinLock,從iOS10開始才支持
從底層調用看,等待os_unfair_lock鎖的線程會處於休眠狀態,並不是忙等
須要導入頭文件#import<os/lock.h>
三、pthread_mutex
mutex叫作」互斥鎖」,等待鎖的線程會處於休眠狀態
須要導入頭文件#import<pthread.h>
pthread_mutex–普通鎖
pthread_mutex–遞歸鎖
pthread_mutex–條件
四、NSLock
五、NSRecursiveLock
NSRecursiveLock也是對mutex遞歸鎖的封裝,API跟NSLock基本一致web
六、NSCondition
NSCondition是對mutex和cond的封裝
七、NSConditionLock
NSConditionLock是對NSCondition的進一步封裝,能夠設置具體的條件值
八、dispatch_semaphore
semaphore叫作」信號量」
信號量的初始值,能夠用來控制線程併發訪問的最大數量
信號量的初始值爲1,表明同時只容許1條線程訪問資源,保證線程同步
九、dispatch_queue(DISPATCH_QUEUE_SERIAL)
直接使用GCD的串行隊列,也是能夠實現線程同步的
十、@synchronized
@synchronized是對mutex遞歸鎖的封裝
@synchronized(obj)內部會生成obj對應的遞歸鎖,而後進行加鎖、解鎖操做
2、iOS線程同步方案性能比較
原則:
普通鎖比遞歸鎖性能好
語言越高級,封裝的邏輯越多,性能也就越差(全部語言都如此)
實際測試
性能從高到低排序
os_unfair_lock // 缺點:iOS10才支持
OSSpinLock // 缺點:可能出現優先級反轉 已經再也不安全 蘋果也不推薦使用
dispatch_semaphore // 推薦使用
pthread_mutex // 優勢:跨平臺 互斥鎖(普通鎖) 推薦使用
dispatch_queue(DISPATCH_QUEUE_SERIAL) // c
NSLock // oc
NSCondition // oc
pthread_mutex(recursive) // 遞歸鎖
NSRecursiveLock // oc
NSConditionLock // oc
@synchronized // 遞歸鎖 oc
自旋鎖:等待狀態處於忙等
互斥鎖:等待狀態處於休眠
一、什麼狀況使用自旋鎖比較划算?
預計線程等待鎖的時間很短
加鎖的代碼(臨界區)常常被調用,但競爭狀況不多發生
CPU資源不緊張
多核處理器
二、什麼狀況使用互斥鎖比較划算?
預計線程等待鎖的時間較長
單核處理器
臨界區有IO操做
臨界區代碼複雜或者循環量大
臨界區競爭很是激烈
4、讀寫鎖
場景:
同一時間,只能有1個線程進行寫的操做
同一時間,容許有多個線程進行讀的操做
同一時間,不容許既有寫的操做,又有讀的操做
上面的場景就是典型的「多讀單寫」,常常用於文件等數據的讀寫操做,iOS中的實現方案有:
一、讀寫鎖:
pthread_rwlock
等待鎖的線程會進入休眠安全
二、dispatch_barrier_async併發
這個函數傳入的併發隊列必須是本身經過dispatch_queue_cretate建立的app
若是傳入的是一個串行或是一個全局的併發隊列,那這個函數便等同於dispatch_async函數的效果async