5000
, 取2500
, 因此應該剩3500
, 可是結果剩了2500
2000
, 這就是多線程的安全隱患問題, 是數據錯亂10
張, 應該剩餘0
張, 可是結果卻剩餘3
張, 說明數據出現了錯亂OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized
複製代碼
多線程安全隱患示例01 – 存錢取錢
和多線程安全隱患示例02 – 賣票
代碼封裝到一個BaseDemo
類中, 具體代碼以下圖BaseDemo
暴露出五個方法, 兩個測試調用, 三個線程調用AddLockDemo
繼承自BaseDemo
ViewController
中代碼以下OSSpinLock
叫作自旋鎖
,等待鎖的線程會處於忙等(busy-wait)狀態,一直佔用着CPU資源存錢取錢
和賣票
的安全隱患存錢取錢
和賣票
中加入OSSpinLock
OSSpinLock
目前已經再也不安全,可能會出現優先級反轉問題thread1
、thread2
和thread3
thread1
執行一段時間後, 接着讓thread2
執行一段時間, 而後再讓thread3
執行一段時間假設經過OSSpinLock給兩個線程`thread1`和`thread2`加鎖
thread優先級高, thread2優先級低
若是thread2先加鎖, 可是尚未解鎖, 此時CPU切換到`thread1`
由於`thread1`的優先級高, 因此CPU會更多的給`thread1`分配資源, 這樣每次`thread1`中遇到`OSSpinLock`都處於使用狀態
此時`thread1`就會不停的檢測`OSSpinLock`是否解鎖, 就會長時間的佔用CPU
這樣就會出現相似於死鎖的問題
複製代碼
os_unfair_lock
用於取代不安全的OSSpinLock, 從iOS10
開始才支持os_unfair_lock
鎖的線程會處於休眠狀態, 並不是忙等#import <os/lock.h>
// 初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
// 嘗試加鎖, 若是lcok已經被使用, 加鎖失敗返回false, 若是加鎖成功, 返回true
os_unfair_lock_trylock(&lock);
// 加鎖
os_unfair_lock_lock(&lock);
// 解鎖
os_unfair_lock_unlock(&lock);
複製代碼
存錢取錢
和賣票
的安全隱患os_unfair_lock
mutex
叫作互斥鎖
,等待鎖的線程會處於休眠狀態#import <pthread.h>
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化鎖
pthread_mutex_t pthread;
pthread_mutex_init(&pthread, &attr);
// 銷燬屬性
pthread_mutexattr_destroy(&attr);
// 銷燬鎖
pthread_mutex_destroy(&pthread);
複製代碼
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
複製代碼
存錢取錢
和賣票
的安全隱患PthreadTest
類繼承自NSObject
, 其中recursive
是一個遞歸方法ViewController
中代碼以下, 點擊屏幕後調用PthreadTest
的recursive
方法recursive
中調用recursive
, 此時尚未解鎖, 再次進行加鎖, 因此發生了死鎖pthread
初始化時的屬性類型爲PTHREAD_MUTEX_RECURSIVE
, 這樣pthread
就是一把遞歸鎖PthreadTest
中代碼以下ViewController
中代碼以下當點擊屏幕時, 會在array
中移除最後一個元素
和添加一個新元素
, 代碼中能夠看到, 使用不一樣線程調用__remove
和__add
兩個方法安全
如今的需求是, 只有在array
不爲空的狀況下, 才能執行刪除操做, 若是直接運行, 那麼可能會先調用__remove
在調用__add
, 那麼就與需求相違背bash
因此, 咱們可使用條件
對兩個方法進行優化多線程
建立cond
函數
array.count == 0
時, 是程序進入休眠, 只有當array
中添加了新數據後在發起信號, 將休眠的線程喚醒__remove
方法, 可是卻在__add
中添加新元素以後再移除元素NSLock
、NSRecursiveLock
、NSCondition
和NSConditionLock
是基於pthread
封裝的OC對象AddLockDemo
中代碼以下, 直接使用NSLock
進行加鎖ViewController
中點擊屏幕時調用方法GNUStep
中關於NSLock
的底層代碼, 能夠看到NSLock
是基礎pthread
封裝的normal
鎖PthreadTest
中代碼以下, 使用NSRecursiveLock
對遞歸函數
加鎖解鎖ViewController
中, 當點擊屏幕時調用recursive
方法遞歸鎖
的結果GNUStep
中關於NSRecursiveLock
的底層代碼PthreadTest
中代碼以下, 使用NSCondition
加鎖解鎖ViewController
中, 當點擊屏幕時調用pthreadTest
方法__remove
方法, 可是卻在__add
中給array
添加了新元素以後, 才刪除一個元素
GNUStep
中關於NSCondition
的底層代碼NSConditionLock
是對NSCondition
的進一步封裝@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
// 初始化, 同時設置 condition
- (instancetype)initWithCondition:(NSInteger)condition;
// condition值
@property (readonly) NSInteger condition;
// 只有NSConditionLock實例中的condition值與傳入的condition值相等時, 才能加鎖
- (void)lockWhenCondition:(NSInteger)condition;
// 嘗試加鎖
- (BOOL)tryLock;
// 嘗試加鎖, 只有NSConditionLock實例中的condition值與傳入的condition值相等時, 才能加鎖
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
// 解鎖, 同時設置NSConditionLock實例中的condition值
- (void)unlockWithCondition:(NSInteger)condition;
// 加鎖, 若是鎖已經使用, 那麼一直等到limit爲止, 若是過期, 不會加鎖
- (BOOL)lockBeforeDate:(NSDate *)limit;
// 加鎖, 只有NSConditionLock實例中的condition值與傳入的condition值相等時, 才能加鎖, 時間限制到limit, 超時加鎖失敗
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
// 鎖的name
@property (nullable, copy) NSString *name;
@end
複製代碼
NSConditionLock
設置線程的執行順序ViewController
代碼以下dispatch_semaphore_t
設置信號量爲1
, 來控制贊成之間只有一條線程能執行, 實際代碼以下@synchronized
是對mutex
遞歸鎖的封裝objc4
中的objc-sync.mm
文件@synchronized(obj)
內部會生成obj
對應的遞歸鎖,而後進行加鎖、解鎖操做@synchronized
進行加鎖objc_sync_enter
和objc_sync_exit
兩個函數, 分別用於加鎖和解鎖SyncData
recursive_mutex_tt
recursive_mutex_tt
, 能夠看到底層是經過os_unfair_recursive_lock
封裝的鎖對象
獲取鎖的代碼LIST_FOR_OBJ
, 點擊查看對象
, 會獲取惟一標識所謂鎖性能從高到低排序
os_unfair_lock
OSSpinLock
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized
複製代碼