多線程
進(jìn)程:
1,是一個(gè)具有一定獨(dú)立功能的程序,操作系統(tǒng)分配資源的基本單位
2,在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,就是一段程序執(zhí)行過程,我們可以理解為手機(jī)上的一個(gè)app
3,每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi),擁有獨(dú)立運(yùn)行所需的全部資源
線程:
1,程序執(zhí)行流的最小單位,線程是進(jìn)程中的一個(gè)實(shí)體
2,一個(gè)進(jìn)程要想執(zhí)行任務(wù),必須至少要有一條線程,應(yīng)用程序啟動(dòng)的時(shí)候系統(tǒng)會(huì)默認(rèn)開啟一跳線程,這就是主線程
進(jìn)程和線程的關(guān)系
1,線程是進(jìn)程的執(zhí)行單元,進(jìn)程的所有任務(wù)都在線程中執(zhí)行
2,線程是cpu分配資源和調(diào)度的最小單位
3,一個(gè)程序可以對(duì)應(yīng)多個(gè)進(jìn)程,一個(gè)進(jìn)程可以有多個(gè)線程,但至少要有一個(gè)線程
4,同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程資源
多進(jìn)程,多線程
1,進(jìn)程是程序在計(jì)算機(jī)上的一個(gè)執(zhí)行活動(dòng),當(dāng)你運(yùn)行一個(gè)程序,你就啟動(dòng)了一個(gè)進(jìn)程,顯然程序是死的(靜態(tài)的),進(jìn)程是活的(動(dòng)態(tài)的)
2,進(jìn)程分為系統(tǒng)進(jìn)程和用戶進(jìn)程,用于完成操作系統(tǒng)的各種功能的進(jìn)程是系統(tǒng)進(jìn)程,所有由用戶啟動(dòng)的進(jìn)程都是用戶進(jìn)程,進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的單位
3,進(jìn)程又被細(xì)化為線程,也就是一個(gè)進(jìn)程下有多個(gè)能獨(dú)立運(yùn)行的更小單位,在同一個(gè)時(shí)間同一個(gè)計(jì)算機(jī)系統(tǒng)中如果允許兩個(gè)或兩個(gè)以上的進(jìn)程出于運(yùn)行狀態(tài),這便是多線程。
1,同一時(shí)間,cpu只能處理一條線程,只有1條線程在執(zhí)行。多線程并發(fā)執(zhí)行,其實(shí)是cpu快速的在多條線程之間調(diào)度(切換),如果cpu調(diào)度線程的時(shí)間足夠快,就能造成多線程并發(fā)執(zhí)行的假象
2,如果線程非常非常多,cpu會(huì)在N多線程之間調(diào)度,消耗大量的cpu資源,每條線程被調(diào)度執(zhí)行的頻次會(huì)降低(線程的執(zhí)行效率降低)
3,優(yōu)點(diǎn):能適當(dāng)提高程序的執(zhí)行效率,能適當(dāng)提高資源利用率
4,缺點(diǎn):開啟線程會(huì)占用一定的內(nèi)存(主線程1m 子線程512kb),如果開啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能,線程越多,cpu在調(diào)度線程上的開銷就越大,程序設(shè)計(jì)的更加復(fù)雜:比如線程之間的通信,多線程的數(shù)據(jù)共享
任務(wù)、隊(duì)列
任務(wù):就是執(zhí)行操作的意思,執(zhí)行任務(wù)的方式有兩種:同步執(zhí)行(sync)和異步執(zhí)行(async)
同步:同步添加任務(wù)到指定的隊(duì)列中,在添加的任務(wù)執(zhí)行結(jié)束之前,會(huì)一直等待,知道隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行,即會(huì)阻塞線程,只能在當(dāng)前線程中執(zhí)行任務(wù)(是當(dāng)前線程,不一定是主線程),不具備開啟新線程的能力
異步:線程會(huì)立即返回,無需等待就會(huì)繼續(xù)執(zhí)行下面的任務(wù),不阻塞當(dāng)前線程,可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力(并不一定開啟新線程),如果不是添加到主隊(duì)列上,異步會(huì)在子線程中執(zhí)行任務(wù)
隊(duì)列
這里的隊(duì)列指執(zhí)行任務(wù)的等待隊(duì)列,用來存放任務(wù)的隊(duì)列,隊(duì)列是一種特殊的線性標(biāo),采用先進(jìn)先出的原則,新任務(wù)總是被插入到隊(duì)列的末尾,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取,每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)
在GCD中有兩種隊(duì)列,串行隊(duì)列和并發(fā)隊(duì)列,兩者都符合先進(jìn)先出的原則,兩者的主要區(qū)別是執(zhí)行順序不同,以及開啟線程數(shù)不同。
串行隊(duì)列:同一時(shí)間,隊(duì)列中只能執(zhí)行一個(gè)任務(wù),只有當(dāng)前的任務(wù)執(zhí)行完成之后,才能執(zhí)行下一個(gè)任務(wù),主隊(duì)列是主線程上的一個(gè)串行隊(duì)列,是系統(tǒng)自動(dòng)為我們創(chuàng)建的
并發(fā)隊(duì)列:同時(shí)允許多個(gè)任務(wù)并發(fā)執(zhí)行,并發(fā)隊(duì)列的并發(fā)功能只有在異步函數(shù)下才有效
iOS中的多線程
主要有三種:NSThread、NSoperationQueue、GCD
NSThread:輕量級(jí)別的多線程技術(shù)
是我們自己手動(dòng)開辟的子線程,如果使用的是初始化方式就需要我們自己啟動(dòng),如果使用的是構(gòu)造器方式他就會(huì)自動(dòng)啟動(dòng),只要是我們手動(dòng)開辟的線程,都需要我們自己管理該線程,不只是啟動(dòng),還有該線程使用完畢后的資源回收
image.png
GCD對(duì)比NSOprationQueue
1,gcd執(zhí)行效率更高,而且由于隊(duì)列中執(zhí)行的是block構(gòu)成的任務(wù),這是一個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu),寫起來方便
2,gcd只支持先進(jìn)先出FIFO的隊(duì)列,而且NSOperationQueue可以通過設(shè)置最大并發(fā)數(shù),設(shè)置優(yōu)先級(jí),添加依賴關(guān)系等調(diào)整執(zhí)行順序
3,NSOperationQueue甚至可以跨隊(duì)列設(shè)置依賴關(guān)系,但是gcd只能通過設(shè)置串行隊(duì)列,或者在隊(duì)列內(nèi)添加barrier任務(wù),才能控制執(zhí)行順序,較為復(fù)雜
4,NSOperationQueue面向?qū)ο螅灾С諯VO,可以監(jiān)測(cè)operation是否正在執(zhí)行 是否結(jié)束 是否取消
(十幾開發(fā)中很多只會(huì)用到異步操作,不會(huì)有特別復(fù)雜的線程關(guān)系管理,所以蘋果推薦優(yōu)化完善,運(yùn)行快速的gcd是首選 如果考慮異步操作之間的事務(wù)性,順序性,依賴關(guān)系,gcd需要自己寫很多的代碼來實(shí)現(xiàn),而NSOperationQueue已經(jīng)內(nèi)建了這些支持,NSThread需要我們自己去管理線程的生命周期,還要考慮線程同步,加鎖問題,造成一些性能上的開銷)
GCD
GCD共有三種隊(duì)列類型
main queue:通過dispatch-get-main-queue()獲得,這是一個(gè)與主線程相關(guān)的串行隊(duì)列
global queue:全局隊(duì)列是并發(fā)隊(duì)列,由整個(gè)進(jìn)程共享,存在高中低三種優(yōu)先級(jí)的全局隊(duì)列,調(diào)用dispath-get-global-queue并傳入優(yōu)先級(jí)來訪問隊(duì)列
自定義隊(duì)列:通過函數(shù)dispatch-queue-create創(chuàng)建的隊(duì)列
死鎖
死鎖就是隊(duì)列引起的循環(huán)等待
一個(gè)比較常見的死鎖例子:主隊(duì)列同步
image.png
同樣下面的代碼也會(huì)造成死鎖:
image.png
外面的函數(shù)無論是同步還是異步都會(huì)造成死鎖
這是因?yàn)槔锩娴娜蝿?wù)和外面的任務(wù)都在同一個(gè)隊(duì)列內(nèi)
又是同步,這就和上邊主隊(duì)列同步的例子一樣造成了死鎖
解決方法也和上邊一樣,將里面的同步改成異步或者將隊(duì)列換成串行或并行
GCD任務(wù)執(zhí)行順序
串行隊(duì)列先異步后同步
image.png
打印順序: 13245
原因是: 首先先打印 1 接下來將任務(wù) 2 其添加至串行隊(duì)列上,由于任務(wù) 2 是異步,不會(huì)阻塞線程,繼續(xù)向下執(zhí)行,打印 3 然后是任務(wù) 4,將任務(wù) 4 添加至串行隊(duì)列上,因?yàn)槿蝿?wù) 4 和任務(wù) 2 在同一串行隊(duì)列,根據(jù)隊(duì)列先進(jìn)先出原則, 任務(wù) 4 必須等任務(wù) 2 執(zhí)行后才能執(zhí)行,又因?yàn)槿蝿?wù) 4 是同步任務(wù),會(huì)阻塞線程,只有執(zhí)行完任務(wù) 4 才能繼 續(xù)向下執(zhí)行打印 5 所以最終順序就是 13245。 這里的任務(wù) 4 在主線程中執(zhí)行,而任務(wù) 2 在子線程中執(zhí)行。 如果任務(wù) 4 是添加到另一個(gè)串行隊(duì)列或者并行隊(duì)列,則任務(wù) 2 和任務(wù) 4 無序執(zhí)行(可以添加多個(gè)任務(wù)看效果)
performSelector
這個(gè)方法要?jiǎng)?chuàng)建提交任務(wù)到runloop上的,而gcd底層創(chuàng)建的線程是默認(rèn)沒有開啟對(duì)應(yīng)runloop的,所有的這個(gè)方法就會(huì)失效
如果改為主隊(duì)列,所在的主線程是默認(rèn)開啟runloop的
延時(shí)函數(shù)
dispatch-after能讓我們添加進(jìn)隊(duì)列的任務(wù)延時(shí)執(zhí)行,該函數(shù)并不是在指定時(shí)間后執(zhí)行處理,而只是在指定時(shí)間追加處理到dispatch-queue
使用dispatch-once實(shí)現(xiàn)單例

NSOperationQueue的有點(diǎn)
NSOperation、NSOperationQueue是蘋果提供給我們的一套多線程解決方案,十幾上他們是基于GCD更高一層的封裝,完全面對(duì)對(duì)象,但是比GCD更簡單易用、代碼可讀性也更高
1,可以添加任務(wù)依賴,方便控制執(zhí)行順序
2,可以設(shè)定操作執(zhí)行的優(yōu)先級(jí)
3,任務(wù)執(zhí)行狀態(tài)控制:isready,isexecuting,isfinished,iscancelled
如果只是重寫NSOperation的main方法,由底層控制變更任務(wù)執(zhí)行及完成狀態(tài),以及任務(wù)退出。
如果重寫了NSOperation的start方法,自行控制任務(wù)狀態(tài)
系統(tǒng)通過KVO的方式移除isfinished==YES的NSOperation
4,可以設(shè)置最大并發(fā)量
NSOperation 和 NSOperationQueue
操作(operation):
執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼
在GCD中是放在block中的,在NSOperation中,使用NSOperation子類NSInvocationOperation、 NSBlockOperation,或者自定義子類來封裝操作。
操作隊(duì)列(operation queue):
這里的隊(duì)列指操作隊(duì)列,即用來存放操作的隊(duì)列。不同于 GCD 中的調(diào)度隊(duì)列 FIFO(先進(jìn)先出)的原則。 NSOperationQueue 對(duì)于添加到隊(duì)列中的操作,首先進(jìn)入準(zhǔn)備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴 關(guān)系),然后進(jìn)入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對(duì)的優(yōu)先級(jí)決定(優(yōu) 先級(jí)是操作對(duì)象自身的屬性)。
操作隊(duì)列通過設(shè)置最大并發(fā)操作數(shù)(maxConcurrentOperationCount)來控制并發(fā)、串行。 NSOperationQueue 為我們提供了兩種不同類型的隊(duì)列:主隊(duì)列和自定義隊(duì)列。主隊(duì)列運(yùn)行在主線程之上, 而自定義隊(duì)列在后臺(tái)執(zhí)行。
自旋鎖與互斥鎖
自旋鎖: 是一種用于保護(hù)多線程共享資源的鎖,與一般互斥鎖(mutex)不同之處在于當(dāng)自旋鎖嘗試獲取鎖時(shí)以忙等 待(busy waiting)的形式不斷地循環(huán)檢查鎖是否可用。當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖住), 那么下一個(gè)線程會(huì)一直等待(不會(huì)睡眠),當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢,下一個(gè)線程會(huì)立即執(zhí)行。 在多 CPU 的環(huán)境中,對(duì)持有鎖較短的程序來說,使用自旋鎖代替一般的互斥鎖往往能夠提高程序的性能。
互斥鎖: 當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖住),那么下一個(gè)線程會(huì)進(jìn)入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢, 當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢,下一個(gè)線程會(huì)自動(dòng)喚醒然后執(zhí)行任務(wù)。
總結(jié): 自旋鎖會(huì)忙等: 所謂忙等,即在訪問被鎖資源時(shí),調(diào)用者線程不會(huì)休眠,而是不停循環(huán)在那里,直到被鎖 資源釋放鎖。 互斥鎖會(huì)休眠: 所謂休眠,即在訪問被鎖資源時(shí),調(diào)用者線程會(huì)休眠,此時(shí) cpu 可以調(diào)度其他線程工 作。直到被鎖資源釋放鎖。此時(shí)會(huì)喚醒休眠線程。
優(yōu)缺點(diǎn):
自旋鎖的優(yōu)點(diǎn)在于,因?yàn)樽孕i不會(huì)引起調(diào)用者睡眠,所以不會(huì)進(jìn)行線程調(diào)度,CPU 時(shí)間片輪轉(zhuǎn)等耗時(shí)操 作。所有如果能在很短的時(shí)間內(nèi)獲得鎖,自旋鎖的效率遠(yuǎn)高于互斥鎖。 缺點(diǎn)在于,自旋鎖一直占用 CPU,他在未獲得鎖的情況下,一直運(yùn)行--自旋,所以占用著 CPU,如果不 能在很短的時(shí) 間內(nèi)獲得鎖,這無疑會(huì)使 CPU 效率降低。自旋鎖不能實(shí)現(xiàn)遞歸調(diào)用。 自旋鎖:atomic、OSSpinLock、dispatch_semaphore_t 互斥鎖:pthread_mutex、@ synchronized、NSLock、NSConditionLock 、NSCondition、NSRecursiveLock



