iOS多線程基礎(chǔ)
在iOS中,多線程的概念應(yīng)該算是比好理解的了.并沒有想象中那么難搞.簡單的概念像線程/進(jìn)程這些概念就不說了.
多線程其實真對單核的CPU來設(shè)計的.CPU同一時間只能執(zhí)行一條線程.而多線程就是讓CPU快速的在多個線程之間進(jìn)行調(diào)度
多線程優(yōu)點:
能夠適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率
能夠適當(dāng)提高資源利用率
缺點:
開啟線程需要一定的內(nèi)存空間,默認(rèn)的一條線程占用棧區(qū)內(nèi)存512KB
線程過多會導(dǎo)致CPU在線程上調(diào)度上的開銷比較大
程序設(shè)計比較復(fù)雜,比如線程間的通信/多線程的數(shù)據(jù)共享
iOS應(yīng)用程序中都是一個主線程,也成為UI線程
那么主線程的作用就是用來更新UI,顯示/刷新UI界面
處理UI時間: 比如點擊/滾動/拖拽/手勢
注意:不能將耗時操作放在主線程,這樣會給用戶一種卡的"體驗"
說下串行,并行,其實很簡單就像你用瀏覽器下載和用迅雷下載,瀏覽器下載一般都是一個接一個的去下載.而迅雷就會同時去下載你所需要的所有東西.
簡單入門級的多線程演示:
touchBegin{
[self ?performselector:@selector(nsoperation) ?withObject:nil ]
}
- (void) nsoperation {
for (int i = 0 , i < 50000 , i++){
nslog(" %d ?%d",i, [NSTread currentThread]);
? ?}
}
iOS多線程技術(shù)--pthread
pthread是一套C語言的多線程API 使用難度比較大 需要程序員手動管理線程的生命周期 ?能夠跨平臺/可移植 ?能夠在UNIX/LINX/Windows 下運行 ?
但是使用起來比較困難,參數(shù)比較難懂 可參照 http://www.baidu.baike.com
iOS多線程--NSThread
是一套OC框架 ?使用更加面向?qū)ο?簡單易用,可直接操作線程對象 ? 偶爾使用
需要程序員手動管理線程的聲明周期 ?
一般用來查看當(dāng)前線程是否是主線程:
=1 當(dāng)前線程是主線程,!=1 當(dāng)前線程是在子線程
iOS多線程--GCD
旨在代替NSThread多線程技術(shù),充分利用設(shè)備的多核
也是一套基于C語言的API ?不需要程序員手動管理線程的聲明周期 ? 經(jīng)常使用
iOS多線程--NSOperation
是一套OC的API,是對GCD的封裝 使用更加面向?qū)ο?? 不需要程序員手動管理線程的聲明周期
比GCD多一些更加簡單實用的功能 ?也是經(jīng)常使用 ?特別是在iOS4以后
iOS多線程中的鎖
互斥鎖:
@synchronized ?是為了保證其中代碼只有一天線程來執(zhí)行
所以@synchronized的范圍是越小效率才高
@synchronized()其中的參數(shù) ?就是能夠加鎖的任意ocject對象
局部變量,是每一條線程單獨擁有的,所以無法加鎖 ? 鎖一定要是所有線程共享的對象
一般使用self ? (全局的也是可以的)
自旋鎖:
自旋鎖存在于原子屬性的內(nèi)部
自旋鎖的特點就是當(dāng)發(fā)現(xiàn)有其他線程正在執(zhí)行鎖定的代碼,該線程會用死循環(huán)的方式去等待鎖定代碼執(zhí)行完畢
atomic(原子屬性): 默認(rèn)是線程安全的 ? ?當(dāng)多個線程寫入屬性時,保證同一時間只有一條線程能夠執(zhí)行寫入操作
線程間的通訊
在NSThread中線程間的通訊
方法: performSelectorOnMainThread: with object: waitUnitDone:
三個參數(shù): 方法選擇器,將該方法放到主線程執(zhí)行
? ? ? ? ? ? ? 傳遞給方法的參數(shù)
? ? ? ? ? ? ? ?是否等待被調(diào)用方法執(zhí)行完成, 有可能也會等待調(diào)用方法執(zhí)行完成! 幾率極小!
NSThread的三種線程創(chuàng)建方法:
1. NSThread * thread = [[NSThread alloc]initWithTarget: selector: object:] ? 創(chuàng)建初始化一條線程
?啟動線程 ? ?[thread start]
2. [NSThread detachNewThreadSelector: toTarget: withObject:] ?會立即執(zhí)行selector方法
就是會分離一個子線程取執(zhí)行selector方法
3.[self performSelectorInbackground: withObject: ]
這是NSObject的一個分類方法,所有NSObject都可以使用此方法在其他線程執(zhí)行方法,一旦指定,就會立即在后臺線程執(zhí)行方法 ? 使用比較靈活
介紹下NSThread中的幾個重要屬性:
name: 一般在比較復(fù)雜的商業(yè)應(yīng)用中會設(shè)置線程的名字,因為可以快速找出崩潰線程
isMainThread: 這個是用來檢測是否是主線程的屬性
threadPriority: 這個屬性是用來設(shè)置線程的優(yōu)先級(線程的優(yōu)先級是 用一個浮點數(shù)來表示: 0~1.0 , 默認(rèn)線程的優(yōu)先級是0.5) 注意: 設(shè)置線程的優(yōu)先級高,只是CPU會優(yōu)先調(diào)用該線程,調(diào)用該線程的可能性會高,并不是會將該線程執(zhí)行完后再去執(zhí)行別的線程 .
線程的狀態(tài)屬性:
isExecuting: 只讀屬性,線程是否正在運行
isFinished: 只讀屬性,線城是否完成
isCancelled: 只讀屬性,線程是否被取消
GCD--Grand Central Dispatch
是蘋果公司為 多核的并行運算提出的 解決方案
GCD是輕量級的多線程技術(shù)
系統(tǒng)會自動管理線程的生命周期
GCD中2個核心概念:
任務(wù): 同步/異步
隊列: 串行/并發(fā)
程序員只需做兩件事: 1. 定制任務(wù),確定需要做的事情
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2. 將任務(wù)添加到隊列中( GCD會自動將任務(wù)從隊列中取出,放到對應(yīng)的線程, 任務(wù)的取出是遵循FIFO原則的:先進(jìn)先出,后進(jìn)后出)
調(diào)度任務(wù),執(zhí)行任務(wù)的方法: 同步/異步
同步: 一個任務(wù)沒有執(zhí)行完,就不會執(zhí)行下一個任務(wù)
異步: 不用等待當(dāng)前執(zhí)行任務(wù)完成,就可以執(zhí)行下一個任務(wù)
任務(wù)隊列組合:
1.串行隊列,同步任務(wù)
不會開啟線程,順序執(zhí)行任務(wù)
2.串行隊列,異步任務(wù)
會開啟一條線程,順序執(zhí)行任務(wù)
3.并發(fā)隊列,異步任務(wù)
會開啟多條線程,每條線程都會執(zhí)行任務(wù),所以不會順序
4.并發(fā)隊列,同步任務(wù)
不會開線程,,順序執(zhí)行
小結(jié): - 開不開線程,取決于任務(wù)是同步還是異步,同步不開,異步開
? ? ? ?- 開幾條線程,取決于隊列,串行開一條,并發(fā)開多條(異步任務(wù))
主隊列特點: 主隊列不是主線程
主線程上有任務(wù),主隊列不會調(diào)度隊列中的任務(wù)
主隊列異步任務(wù) : 等待主線程上的任務(wù)執(zhí)行完畢會執(zhí)行主隊列中的任務(wù)
主隊列同步任務(wù) : 等待主線程上的任務(wù)執(zhí)行完畢,同步任務(wù):當(dāng)前任務(wù)不執(zhí)行完畢,不會執(zhí)行下一條任務(wù)
所以會造成互相等待,死鎖!!!
同步任務(wù)的特點:
- 可以在隊列調(diào)度多個異步任務(wù)之前,指定一個同步任務(wù),讓所有異步任務(wù)等待同步任務(wù)執(zhí)行完畢,這就是依賴關(guān)系
全局隊列:(其實就是并發(fā)隊列<感覺是根據(jù)任務(wù)的同步/異步來確定的>)
就是給程序員使用的,方便程序員使用的
全局隊列和并發(fā)隊列的區(qū)別:
1>名稱(隊列后邊的參數(shù)<是個字符串其實就是隊列的名稱>),并發(fā)隊列有名稱,適合商業(yè)級軟件跟蹤錯誤報告
2>release , 在MRC開發(fā)時,并發(fā)隊列需要使用 dispatch_release(q);
目前絕大多數(shù)的軟件都會使用全局隊列. 比如比較優(yōu)秀的第三方框架會使用自定義的并發(fā)隊列!
全局隊列和串行隊列的選擇
全局隊列: ? 并發(fā),能夠調(diào)度多個線程,執(zhí)行效率高
? ? ? ? ? ? ? ?- 比較費電
串行隊列: ? 一個接一個,只能夠開啟一條線程,執(zhí)行效率比較低
? ? ? ? ? ? ? ?- 省電,省錢,省流量
所以選擇是根據(jù)用戶連網(wǎng)狀態(tài)來選擇:
- WIFI,可以多開線程, 6條
- 3G/4G,盡量少開線程,2~3條
延時操作:
dispatch_after(dispatch_time,(int64_t)(delayInSeconds * NSEC_PER_SEC)),
? ? ? ? ? ?dispatch_get_main_queue(),^{
});
一次性操作: ? 蘋果提供了一個一次性執(zhí)行的機(jī)制,不僅能夠保證只被執(zhí)行一次,而且是"線程安全"的
dispatch_once()
使用互斥鎖也可以,但是效率低
調(diào)度組:
dispatch_group_t
可以將多個任務(wù)放在同一個調(diào)度組中,同時監(jiān)聽這多個任務(wù)的下載,最后用dispatch_group_notify來通知
其實就是用一個調(diào)度組,可以監(jiān)聽全局隊列調(diào)度的任務(wù),執(zhí)行完畢后,在主隊列執(zhí)行最終處理
dispatch_group_notify本身是異步的,這個還可以用另一種方法還實現(xiàn)是:dispatch_group_wait()其中第二個參數(shù)就是等待時間,一般寫DISPATCH_TIME_FOREVER
第二種使用方法,經(jīng)常出現(xiàn)在三方框架中:AFN
dispatch_group_enter() ? ? ?進(jìn)入群組
dispatch_group_leave() ? ? ? 退出群組
這兩個方法必須成對出現(xiàn)
NSOperation
核心概念就是 將"操作"添加到"隊列"
NSOperation是一個抽象類,不能直接使用!
它是對GCD的面向?qū)ο蟮牡姆庋b
目的:定義子類共有的屬性和方法
子類: - NSInvocationOperation
? ? ? ?- NSBlockOperation
NSInvocationOperation:
只需要創(chuàng)建隊列,執(zhí)行start方法,就可以使用.
本質(zhì)上就是GCD中的并發(fā)隊列, 異步任務(wù)的執(zhí)行
NSBlockOperation:
可以直接來一個全局的隊列屬性,懶加載之后,可以對所有的NSOperation的子類,添加到隊列!
回主線程,直接[NSOperationQueue mainQueue]addOperationWithBlock:^ 就可以回主線程更新UI了非常簡單好用
最大并發(fā)操作數(shù):
從iOS8開始,無論是GCD還是NSOperation,都會開啟很多線程,在iOS7以前GCD通常只會開啟5~6條線程
線程數(shù)的增加說明了:
1 >底層的線程池變大了,能夠拿到的線程資源更多了!
2 >對控制同時并發(fā)的線程數(shù),要求更高了.
暫停/繼續(xù)/取消全部操作:
暫停/繼續(xù): ?NSOperation中的屬性isSuspended 是否掛起
可以對isSuspended判斷,判斷隊列是否掛起,來做任務(wù)的暫?;蛘呃^續(xù).
1.在設(shè)置隊列的掛起屬性(isSuspended)時,并不會判斷隊列中是否有操作
所以如果不希望用戶有困惑,可以提前做判斷,就是對隊列的operationCount屬性進(jìn)行判斷
2.在暫停的時候,隊列中的操作數(shù)是包括正在執(zhí)行的操作,再次繼續(xù)的時候,如果之前正在執(zhí)行的操作已經(jīng)完成,那么隊列中的操作數(shù)就只有沒有調(diào)度的操作
取消所有的操作:
通過設(shè)置隊列的cancelAllOperations屬性,可以取消隊列的所有操作
注意: 這個取消屬性,也是不會取消正在執(zhí)行的操作
依賴關(guān)系:
NSOperation提供了依賴關(guān)系
NSOperation的所有操作都是異步的,但是為了建立操作之間的依賴關(guān)系,所以提供了dependency的功能
GCD中可以通過同步任務(wù)來實現(xiàn),也可以通過串行隊列來實現(xiàn)
先設(shè)置以來關(guān)系: op2 addDenpendency: op1 ? 即 ?任務(wù)2依賴任務(wù)1,任務(wù)2會等任務(wù)1完成
將所有操作都加入到隊列中 addOperations:@[op1,op2,op3] ?waitUntilFinished:
withUntilFinished這個參數(shù)類似于GCD中的dispatch_group_wait(g , DISPATCH_TIME_FOREVER) ?是否等待所有操作完成
但是注意: ?不要指定循環(huán)依賴,雖然不會死鎖,但是隊列會不工作!!!以前的Xcode版本會死鎖.
指定依賴關(guān)系是可以跨隊列進(jìn)行的. ?將op1,op2添加到隊列上,將op3添加到主隊列上,一樣按照指定的依賴關(guān)系執(zhí)行.
NSOperation & GCD的對比
GCD ?iOS4.0之后推出的針對多核處理器并做了優(yōu)化的并發(fā)技術(shù),是C語言的
- 將"任務(wù)"添加到"隊列(串行/并發(fā)/全局/主隊列)",并且指定執(zhí)行任務(wù)的函數(shù)(同步/異步)
- 線程間的通訊 ?dispatch_get_main_queue()
- 提供了一些NSOperation不具備的功能
? ? ? ?- 一次性執(zhí)行
? ? ? ?- 延遲操作
? ? ? ?- 在NSOperation中也可實現(xiàn),相對來說比較麻煩
NSOperation是在iOS2.0之后推出的,蘋果推出GCD之后,對NSOperation底層重寫了一遍
- 將"操作"(異步執(zhí)行的任務(wù))添加到隊列(并發(fā)隊列),就會立即異步執(zhí)行
- 線程間的通訊 ?mainQueue
- 提供了一些GCD實現(xiàn)比較困難的功能
? ? ? ? ?- 最大線程并發(fā)數(shù)
? ? ? ? ?- 隊列的暫停/繼續(xù)
? ? ? ? ?- 取消所有操作
? ? ? ? ?- 指定操作之間的依賴關(guān)系 (GCD可以用同步任務(wù)來實現(xiàn))