OC源碼:https://opensource.apple.com/tarballs/objc4/
GCD源碼:https://github.com/apple/swift-corelibs-libdispatch
iOS中常見多線程方案
同步方庭,異步,串行隊列税灌,并發(fā)隊列之間關(guān)系
同步
- 主隊列 (沒有開辟新線程 串行執(zhí)行任務(wù))吹害,主隊列本來是同步串行執(zhí)行任務(wù)螟凭,如果再在主隊列同步執(zhí)行任務(wù),會發(fā)生死鎖
- 并發(fā)隊列 (沒有開辟新線程 串行執(zhí)行任務(wù))
- 手動創(chuàng)建的串行隊列 (沒有開辟新線程 串行執(zhí)行任務(wù))
異步
- 主隊列 (沒有開辟新線程 串行執(zhí)行任務(wù))
- 并發(fā)隊列 (開辟了新線程 并行執(zhí)行任務(wù))
- 手動創(chuàng)建的串行隊列 (開辟了新線程 串行執(zhí)行任務(wù))
隊列組
//任務(wù)1 任務(wù)2 并發(fā)執(zhí)行 都執(zhí)行完畢后在主線程執(zhí)行任務(wù)3
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)1-%d-%@",i,[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)2-%d-%@",i,[NSThread currentThread]);
}
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)3-%d-%@",i,[NSThread currentThread]);
}
});
});
//1 2任務(wù)完成后并發(fā)執(zhí)行 4 5
dispatch_group_notify(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)4-%d-%@",i,[NSThread currentThread]);
}
});
dispatch_group_notify(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)5-%d-%@",i,[NSThread currentThread]);
}
});
多線程安全隱患
資源共享
- 同一塊資源被多個線程訪問
解決方案
常見的線程同步技術(shù)
- OSSpinLock 自旋鎖
等待鎖的線程處于忙等狀態(tài)赠制,一直占用CPU資源赂摆,響應(yīng)快,目前不再安全钟些,有優(yōu)先級反轉(zhuǎn)問題烟号,如果等待鎖的線程優(yōu)先級較高,會一直占用CPU資源政恍,優(yōu)先級低的線程就無法釋放鎖
#import <libkern/OSAtomic.h>
OSSpinLock lock = OS_SPINLOCK_INIT;
//加鎖
OSSpinLockLock(&_lock);
//解鎖
OSSpinLockUnlock(&_lock);
- os_unfair_lock 互斥鎖
等待鎖的線程處于休眠狀態(tài)
#import <os/lock.h>
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//加鎖
os_unfair_lock_lock(&_lock);
//解鎖
os_unfair_lock_unlock(&_lock);
*遞歸鎖:允許同一線程多次加鎖汪拥,另一線程執(zhí)行到鎖會等待
- pthread_mutex
pthread跨平臺,可以修改屬性成為互斥鎖或者遞歸鎖
pthread_cond 條件用于線程間通訊
- NSLock是對pthread_mutex 普通鎖的封裝
//加鎖
- (void)lock;
//解鎖
- (void)unlock;
- (BOOL)tryLock; //嘗試加鎖篙耗,不阻塞線程
- (BOOL)lockBeforeDate:(NSDate *)limit; // 是在指定Date之前嘗試加鎖迫筑,如果在指定時間之前都不能加鎖宪赶,則返回NO。會阻塞線程
NSRecursiveLock是對pthread_mutex 遞歸鎖的封裝
NSCondition是對pthread_mutex和pthread_cond的封裝
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
NS_CLASS_AVAILABLE(10_5, 2_0)
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
- NSConditionLock是對NSCondition的封裝
- GCD 串行隊列
- dispatch_semaphore 信號量
//最大并發(fā)數(shù)是2
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
//判斷當(dāng)前信號量的值是否 > 0, 大于0脯燃,將信號量-1搂妻,執(zhí)行下面的。否則一直休眠等待辕棚。 如果是DISPATCH_TIME_NOW欲主,則大于0,將信號量-1逝嚎,往下執(zhí)行扁瓢,或者小于等于0,則不等补君,往下執(zhí)行
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
//給信號量+1
dispatch_semaphore_signal(self.semaphore);
- @synchronized是對mutex遞歸鎖的封裝
@synchronized (<#token#>) {
<#statements#>
}
1.打斷點得到圖2匯編代碼調(diào)用
2.紅框類似于加鎖解鎖操作
3.在objc源碼objc-sync.mm中找到這兩個方法實現(xiàn)
4.找到結(jié)構(gòu)體SyncData的定義
5.跟進成員recursive_mutex_t
6.跟進recursive_mutex_tt引几,找到構(gòu)造函數(shù),PTHREAD_RECURSIVE_MUTEX_INITIALIZER證明該鎖是遞歸鎖
性能排序
自旋鎖和互斥鎖比較
自旋鎖OSSpinLock已經(jīng)廢棄
選用自旋鎖
(1).預(yù)計線程等待鎖的時間很短
(2).加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用挽铁,但競爭情況很少發(fā)生
(3).CPU資源不緊張時伟桅,多核處理器
選用互斥鎖
(1).預(yù)計線程等待鎖的時間較長
(2).單核處理器
(3).臨界區(qū)有IO操作
(4).臨界區(qū)代碼復(fù)雜
(5).臨界區(qū)競爭激烈
atomic和nonatomic
atomic
(1).給屬性加上atomic,可以保證屬性的set和get都是原子性操作屿储,保證線程同步,查看objc4源碼objc-accessors.mm如下
(2).不能保證使用屬性的過程是線程安全的
(3).太耗性能
讀寫安全方案(多讀單寫)
(1).同一時間贿讹,只能有1個線程進行寫的操作
(2).同一時間,允許有多個線程進行讀的操作
(3).同一時間够掠,不允許既有寫的操作民褂,又有讀的操作
- pthread_rwlock 讀寫鎖
等待鎖的線程會進入休眠
pthread_rwlock_t rwLock ;
//初始化
pthread_rwlock_init(&rwLock, NULL);
//讀加鎖
pthread_rwlock_rdlock(&rwLock);
//寫加鎖
pthread_rwlock_wrlock(&rwLock);
//讀寫解鎖
pthread_rwlock_unlock(&rwLock);
//鎖銷毀
pthread_rwlock_destroy(&rwLock);
- dispatch_barrier_async 異步柵欄調(diào)用
這個函數(shù)傳入的queue必須是自定義的并發(fā)隊列,如果傳入一個串行或者全局并發(fā)隊列疯潭,則等同于dispatch_async的效果
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
//read
dispatch_async(queue, ^{
});
//write
dispatch_barrier_async(queue, ^{
});