a) NSThread / GCD / NSOperation底層都是pthread
b) NSThread開啟線程方式
1) 動(dòng)態(tài)實(shí)例化
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:nil];
thread.threadPriority = 1; //設(shè)置線程的優(yōu)先級(jí)(0.0 - 1.0, 1.0最高級(jí))
[thread start];
2) 靜態(tài)實(shí)例化
[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:nil];
3) 隱式實(shí)例化
3.1)
[self performSelectorOnMainThread:@selector(test:) withObject:nil waitUntilDone:YES];
a) GCD中有2個(gè)用來執(zhí)行任務(wù)的函數(shù)
1) 用同步的方式執(zhí)行任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); // queue:隊(duì)列 block:任務(wù)
1.1) dispatch_sync : 立馬在當(dāng)前線程同步執(zhí)行任務(wù),不執(zhí)行就不會(huì)往下走
2) 用異步的方式執(zhí)行任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
2.1) dispatch_async : 不要求立馬在當(dāng)前線程同步執(zhí)行任務(wù),等上一個(gè)任務(wù)(也可能是整個(gè)外部的大函數(shù)執(zhí)行完畢)執(zhí)行完了再執(zhí)行
b) GCD源碼https://github.com/apple/swift-corelibs-libdispatch
a) GCD的隊(duì)列可以分為2大類型
1) 并發(fā)隊(duì)列(Concurrent Dispatch Queue)
1.1) 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
1.2) 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
b) 串行隊(duì)列(Serial Dispatch Queue)
1) 讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))
2) 主隊(duì)列是一種特殊的串行隊(duì)列
c) 隊(duì)列的特點(diǎn) : 排隊(duì),F(xiàn)IFO盹兢,F(xiàn)irst In First Out 先進(jìn)先出
d) Global Queue全局隊(duì)列的指針地址是相同的,而手動(dòng)創(chuàng)建的隊(duì)列則地址是不相同的
a) 同步和異步主要影響:能不能開啟新的線程
1) 同步:在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
2) 異步:在新的線程中執(zhí)行任務(wù)哥桥,具備開啟新線程的能力
b) 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
1) 并發(fā):多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
2) 串行:一個(gè)任務(wù)執(zhí)行完畢后佃扼,再執(zhí)行下一個(gè)任務(wù)
a) 使用sync函數(shù)往當(dāng)前串行隊(duì)列中添加任務(wù),會(huì)卡住當(dāng)前的串行隊(duì)列(產(chǎn)生死鎖)
b) performSelector:withObject:afterDelay:
1) 有afterDelay的方法都是跟Runloop有關(guān),相當(dāng)于添加了一個(gè)定時(shí)器到Runloop去執(zhí)行 所以你要看當(dāng)前執(zhí)行任務(wù)的線程有沒有Runloop
2) 在主線程好時(shí)正常調(diào)用,但是會(huì)延遲,因?yàn)橹骶€程默認(rèn)就會(huì)開啟一個(gè)Runloop,延遲是因?yàn)橹骶€程的任務(wù)執(zhí)行順序是串行的,需要等上一個(gè)任務(wù)執(zhí)行完畢,可以理解為包裝該線程調(diào)用的{}范圍函數(shù)調(diào)用完畢,例如viewDidLoad方法調(diào)用完畢
3) 但是在子線程卻不好使,直接不調(diào)用,因?yàn)樽泳€程沒有開啟Runloop來蔽⑼酰活
a) 思考:如何用gcd實(shí)現(xiàn)以下功能
1) 異步并發(fā)執(zhí)行任務(wù)1屡限、任務(wù)2
2) 等任務(wù)1、任務(wù)2都執(zhí)行完畢后炕倘,再回到主線程執(zhí)行任務(wù)3
a) 資源共享
1) 1塊資源可能會(huì)被多個(gè)線程共享钧大,也就是多個(gè)線程可能會(huì)訪問同一塊資源
2) 比如多個(gè)線程訪問同一個(gè)對(duì)象、同一個(gè)變量罩旋、同一個(gè)文件
b) 當(dāng)多個(gè)線程訪問同一塊資源時(shí)啊央,很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問題
c) 多線程安全隱患示例01 – 存錢取錢
d) 多線程安全隱患示例02 – 賣票
e) 多線程安全隱患分析
f) 多線程安全隱患的解決方案
1) 解決方案:使用線程同步技術(shù)(同步,就是協(xié)同步調(diào)涨醋,按預(yù)定的先后次序進(jìn)行)
2) 線程同步本質(zhì):是不能同時(shí)讓多條線程占用一個(gè)資源
2) 常見的線程同步技術(shù)是:加鎖(所有線程都用同一把鎖)
3) 鎖的原理:首先判斷這把鎖有沒有被人加過,沒有就會(huì)加鎖,有被加過,就會(huì)等待
a) OSSpinLock(High-level-lock高級(jí)鎖)
1) OSSpinLock叫做”自旋鎖”,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài)(相當(dāng)于執(zhí)行了一個(gè)do while循環(huán))一直占用著CPU資源
2) iOS10.0開始不推薦使用,目前已經(jīng)不再安全瓜饥,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問題(如果等待鎖的線程優(yōu)先級(jí)較高,它會(huì)一直占用著CPU資源浴骂,優(yōu)先級(jí)低的線程就無法釋放鎖,造成死鎖的現(xiàn)象)
3) 需要導(dǎo)入頭文件#import <libkern/OSAtomic.h>
b) os_unfair_lock(Low-level-lock低級(jí)鎖)
1) os_unfair_lock用于取代不安全的OSSpinLock 乓土,從iOS10開始才支持
2) 從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài)溯警,并非忙等
3) 需要導(dǎo)入頭文件#import <os/lock.h>
c) pthread_mutex(Low-level-lock低級(jí)鎖)
1) mutex叫做”互斥鎖”帐我,等待鎖的線程會(huì)處于休眠狀態(tài)
2) 需要導(dǎo)入頭文件#import <pthread.h>
3) pthread_mutex – 遞歸鎖
3.1) 遞歸鎖:允許用同一條線程對(duì)一把鎖進(jìn)行重復(fù)加鎖
4) pthread_mutex – 條件
4.1) 條件(等待條件,等不到條件就休眠,等待的時(shí)候解鎖,喚醒的時(shí)候加鎖,次數(shù)對(duì)等,一般用于線程之間的通信)
d) NSLock
1) NSLock是對(duì)mutex普通鎖的封裝
e) NSRecursiveLock
1) NSRecursiveLock是對(duì)mutex遞歸鎖的封裝,API跟NSLock基本一致
f) NSCondition
1) NSCondition是對(duì)mutex和cond的封裝
2) signal:信號(hào)發(fā)出的時(shí)機(jī),決定了后續(xù)代碼的執(zhí)行,因?yàn)樵诮怄i前和解鎖后
3) signal:信號(hào)(單個(gè))和broadcast:廣播(多個(gè))的區(qū)別
g) NSConditionLock
1) NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝愧膀,可以設(shè)置具體的條件值
2) 初始化不設(shè)置條件值的時(shí)候,默認(rèn)條件值就是0
3) lock:直接加鎖谣光,不等條件值檩淋,如果沒有加鎖,那就加鎖
4) lockWhenCondition:條件值為多少的時(shí)候才加鎖
5) unlockWithCondition:解鎖并且設(shè)置條件值
6) 在子線程執(zhí)行的任務(wù),可以達(dá)到依賴的效果
h) dispatch_semaphore
1) semaphore叫做”信號(hào)量”
2) 信號(hào)量的初始值萄金,可以用來控制線程并發(fā)訪問的最大數(shù)量
3) 信號(hào)量的初始值為1蟀悦,代表同時(shí)只允許1條線程訪問資源,保證線程同步
i) dispatch_queue
1) 直接使用GCD的串行隊(duì)列氧敢,也是可以實(shí)現(xiàn)線程同步的
2) 不是說有多線程就需要加鎖
j) @synchronized
1) @synchronized是對(duì)mutex遞歸鎖的封裝
2) 源碼查看:objc4中的objc-sync.mm文件
3) @synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖日戈,然后進(jìn)行加鎖、解鎖操作
4) 性能比較差,蘋果官方不推薦使用,所以代碼提示也沒有
a) 什么情況使用自旋鎖比較劃算浙炼?
1) 預(yù)計(jì)線程等待鎖的時(shí)間很短
2) 加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用份氧,但競(jìng)爭(zhēng)情況很少發(fā)生
3) CPU資源不緊張
4) 多核處理器
b) 什么情況使用互斥鎖比較劃算?
1) 預(yù)計(jì)線程等待鎖的時(shí)間較長(zhǎng)
2) 臨界區(qū)有IO操作
3) 臨界區(qū)代碼復(fù)雜或者循環(huán)量大
4) 臨界區(qū)競(jìng)爭(zhēng)非常激烈
5) 單核處理器
c) 臨界區(qū):lock與unlock之間的代碼
d) IO操作:文件操作(讀和寫的操作)
a) atomic用于保證屬性setter弯屈、getter的原子性操作蜗帜,相當(dāng)于在getter和setter內(nèi)部加了線程同步的鎖
b) 可以參考源碼objc4的objc-accessors.mm
c) 它并不能保證使用屬性的過程是線程安全的
a) 思考如何實(shí)現(xiàn)以下場(chǎng)景
1) 同一時(shí)間,只能有1個(gè)線程進(jìn)行寫的操作
2) 同一時(shí)間资厉,允許有多個(gè)線程進(jìn)行讀的操作
3) 同一時(shí)間厅缺,不允許既有寫的操作,又有讀的操作
b) 上面的場(chǎng)景就是典型的“多讀單寫”宴偿,經(jīng)常用于文件等數(shù)據(jù)的讀寫操作
c) iOS中的實(shí)現(xiàn)方案有
1) pthread_rwlock:讀寫鎖
2) dispatch_barrier_async:異步柵欄調(diào)用
2.1) 這個(gè)函數(shù)傳入的并發(fā)隊(duì)列必須是自己通過dispatch_queue_cretate創(chuàng)建的
2.2) 如果傳入的是一個(gè)串行或是一個(gè)全局的并發(fā)隊(duì)列湘捎,那這個(gè)函數(shù)便等同于dispatch_async函數(shù)的效果
a) GNUstep是GNU計(jì)劃的項(xiàng)目之一,它將Cocoa的OC庫重新開源實(shí)現(xiàn)了一遍
b) 源碼地址:http://www.gnustep.org/resources/downloads.php
c) 雖然GNUstep不是蘋果官方源碼窄刘,但還是具有一定的參考價(jià)值
14.其它知識(shí)點(diǎn)總結(jié)
a) lldb查看匯編指令級(jí)別代碼時(shí)一行一行的運(yùn)行
1) stepi(si)
2) nexti(ni):也是一行一行,但是遇到函數(shù)調(diào)用會(huì)一筆帶過這個(gè)函數(shù)調(diào)用
3) c:直接跳到下一個(gè)斷點(diǎn)
b) 定義變量的時(shí)候同時(shí)給他賦值結(jié)構(gòu)體可以這么干窥妇,但是不能直接給結(jié)構(gòu)體賦值
c) 線程的任務(wù)一旦執(zhí)行完畢,生命周期就結(jié)束了都哭,無法再使用 // 準(zhǔn)確來講秩伞,使用runloop是為了讓線程保持激活狀態(tài)