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