多線程
進(jìn)程:
1,是一個具有一定獨(dú)立功能的程序箫章,操作系統(tǒng)分配資源的基本單位
2,在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序怕午,就是一段程序執(zhí)行過程,我們可以理解為手機(jī)上的一個app
3橘原,每個進(jìn)程之間是獨(dú)立的,每個進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)涡上,擁有獨(dú)立運(yùn)行所需的全部資源
線程:
1趾断,程序執(zhí)行流的最小單位,線程是進(jìn)程中的一個實體
2吓懈,一個進(jìn)程要想執(zhí)行任務(wù)歼冰,必須至少要有一條線程靡狞,應(yīng)用程序啟動的時候系統(tǒng)會默認(rèn)開啟一跳線程耻警,這就是主線程
進(jìn)程和線程的關(guān)系
1,線程是進(jìn)程的執(zhí)行單元甸怕,進(jìn)程的所有任務(wù)都在線程中執(zhí)行
2甘穿,線程是cpu分配資源和調(diào)度的最小單位
3,一個程序可以對應(yīng)多個進(jìn)程梢杭,一個進(jìn)程可以有多個線程温兼,但至少要有一個線程
4,同一個進(jìn)程內(nèi)的線程共享進(jìn)程資源
多進(jìn)程武契,多線程
1募判,進(jìn)程是程序在計算機(jī)上的一個執(zhí)行活動,當(dāng)你運(yùn)行一個程序咒唆,你就啟動了一個進(jìn)程届垫,顯然程序是死的(靜態(tài)的),進(jìn)程是活的(動態(tài)的)
2全释,進(jìn)程分為系統(tǒng)進(jìn)程和用戶進(jìn)程装处,用于完成操作系統(tǒng)的各種功能的進(jìn)程是系統(tǒng)進(jìn)程,所有由用戶啟動的進(jìn)程都是用戶進(jìn)程浸船,進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的單位
3妄迁,進(jìn)程又被細(xì)化為線程寝蹈,也就是一個進(jìn)程下有多個能獨(dú)立運(yùn)行的更小單位,在同一個時間同一個計算機(jī)系統(tǒng)中如果允許兩個或兩個以上的進(jìn)程出于運(yùn)行狀態(tài)登淘,這便是多線程箫老。
1,同一時間形帮,cpu只能處理一條線程槽惫,只有1條線程在執(zhí)行。多線程并發(fā)執(zhí)行辩撑,其實是cpu快速的在多條線程之間調(diào)度(切換)界斜,如果cpu調(diào)度線程的時間足夠快,就能造成多線程并發(fā)執(zhí)行的假象
2合冀,如果線程非常非常多各薇,cpu會在N多線程之間調(diào)度,消耗大量的cpu資源君躺,每條線程被調(diào)度執(zhí)行的頻次會降低(線程的執(zhí)行效率降低)
3峭判,優(yōu)點:能適當(dāng)提高程序的執(zhí)行效率,能適當(dāng)提高資源利用率
4棕叫,缺點:開啟線程會占用一定的內(nèi)存(主線程1m 子線程512kb)林螃,如果開啟大量的線程,會占用大量的內(nèi)存空間俺泣,降低程序的性能疗认,線程越多,cpu在調(diào)度線程上的開銷就越大伏钠,程序設(shè)計的更加復(fù)雜:比如線程之間的通信横漏,多線程的數(shù)據(jù)共享
任務(wù)、隊列
任務(wù):就是執(zhí)行操作的意思熟掂,執(zhí)行任務(wù)的方式有兩種:同步執(zhí)行(sync)和異步執(zhí)行(async)
同步:同步添加任務(wù)到指定的隊列中缎浇,在添加的任務(wù)執(zhí)行結(jié)束之前,會一直等待赴肚,知道隊列里面的任務(wù)完成之后再繼續(xù)執(zhí)行素跺,即會阻塞線程,只能在當(dāng)前線程中執(zhí)行任務(wù)(是當(dāng)前線程誉券,不一定是主線程)指厌,不具備開啟新線程的能力
異步:線程會立即返回,無需等待就會繼續(xù)執(zhí)行下面的任務(wù)横朋,不阻塞當(dāng)前線程仑乌,可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力(并不一定開啟新線程),如果不是添加到主隊列上晰甚,異步會在子線程中執(zhí)行任務(wù)
隊列
這里的隊列指執(zhí)行任務(wù)的等待隊列衙传,用來存放任務(wù)的隊列,隊列是一種特殊的線性標(biāo)厕九,采用先進(jìn)先出的原則蓖捶,新任務(wù)總是被插入到隊列的末尾,而讀取任務(wù)的時候總是從隊列的頭部開始讀取扁远,每讀取一個任務(wù)俊鱼,則從隊列中釋放一個任務(wù)
在GCD中有兩種隊列,串行隊列和并發(fā)隊列畅买,兩者都符合先進(jìn)先出的原則并闲,兩者的主要區(qū)別是執(zhí)行順序不同,以及開啟線程數(shù)不同谷羞。
串行隊列:同一時間帝火,隊列中只能執(zhí)行一個任務(wù),只有當(dāng)前的任務(wù)執(zhí)行完成之后湃缎,才能執(zhí)行下一個任務(wù)犀填,主隊列是主線程上的一個串行隊列,是系統(tǒng)自動為我們創(chuàng)建的
并發(fā)隊列:同時允許多個任務(wù)并發(fā)執(zhí)行嗓违,并發(fā)隊列的并發(fā)功能只有在異步函數(shù)下才有效
iOS中的多線程
主要有三種:NSThread九巡、NSoperationQueue、GCD
NSThread:輕量級別的多線程技術(shù)
是我們自己手動開辟的子線程蹂季,如果使用的是初始化方式就需要我們自己啟動冕广,如果使用的是構(gòu)造器方式他就會自動啟動,只要是我們手動開辟的線程乏盐,都需要我們自己管理該線程佳窑,不只是啟動制恍,還有該線程使用完畢后的資源回收
image.png
GCD對比NSOprationQueue
1父能,gcd執(zhí)行效率更高,而且由于隊列中執(zhí)行的是block構(gòu)成的任務(wù)净神,這是一個輕量級的數(shù)據(jù)結(jié)構(gòu)何吝,寫起來方便
2,gcd只支持先進(jìn)先出FIFO的隊列鹃唯,而且NSOperationQueue可以通過設(shè)置最大并發(fā)數(shù)爱榕,設(shè)置優(yōu)先級,添加依賴關(guān)系等調(diào)整執(zhí)行順序
3坡慌,NSOperationQueue甚至可以跨隊列設(shè)置依賴關(guān)系黔酥,但是gcd只能通過設(shè)置串行隊列,或者在隊列內(nèi)添加barrier任務(wù),才能控制執(zhí)行順序跪者,較為復(fù)雜
4棵帽,NSOperationQueue面向?qū)ο螅灾С諯VO渣玲,可以監(jiān)測operation是否正在執(zhí)行 是否結(jié)束 是否取消
(十幾開發(fā)中很多只會用到異步操作逗概,不會有特別復(fù)雜的線程關(guān)系管理,所以蘋果推薦優(yōu)化完善忘衍,運(yùn)行快速的gcd是首選 如果考慮異步操作之間的事務(wù)性逾苫,順序性,依賴關(guān)系枚钓,gcd需要自己寫很多的代碼來實現(xiàn)铅搓,而NSOperationQueue已經(jīng)內(nèi)建了這些支持,NSThread需要我們自己去管理線程的生命周期搀捷,還要考慮線程同步狸吞,加鎖問題,造成一些性能上的開銷)
GCD
GCD共有三種隊列類型
main queue:通過dispatch-get-main-queue()獲得指煎,這是一個與主線程相關(guān)的串行隊列
global queue:全局隊列是并發(fā)隊列蹋偏,由整個進(jìn)程共享,存在高中低三種優(yōu)先級的全局隊列至壤,調(diào)用dispath-get-global-queue并傳入優(yōu)先級來訪問隊列
自定義隊列:通過函數(shù)dispatch-queue-create創(chuàng)建的隊列
死鎖
死鎖就是隊列引起的循環(huán)等待
一個比較常見的死鎖例子:主隊列同步
image.png
同樣下面的代碼也會造成死鎖:
image.png
外面的函數(shù)無論是同步還是異步都會造成死鎖
這是因為里面的任務(wù)和外面的任務(wù)都在同一個隊列內(nèi)
又是同步威始,這就和上邊主隊列同步的例子一樣造成了死鎖
解決方法也和上邊一樣,將里面的同步改成異步或者將隊列換成串行或并行
GCD任務(wù)執(zhí)行順序
串行隊列先異步后同步
image.png
打印順序: 13245
原因是: 首先先打印 1 接下來將任務(wù) 2 其添加至串行隊列上像街,由于任務(wù) 2 是異步黎棠,不會阻塞線程,繼續(xù)向下執(zhí)行镰绎,打印 3 然后是任務(wù) 4,將任務(wù) 4 添加至串行隊列上脓斩,因為任務(wù) 4 和任務(wù) 2 在同一串行隊列,根據(jù)隊列先進(jìn)先出原則畴栖, 任務(wù) 4 必須等任務(wù) 2 執(zhí)行后才能執(zhí)行随静,又因為任務(wù) 4 是同步任務(wù),會阻塞線程吗讶,只有執(zhí)行完任務(wù) 4 才能繼 續(xù)向下執(zhí)行打印 5 所以最終順序就是 13245燎猛。 這里的任務(wù) 4 在主線程中執(zhí)行,而任務(wù) 2 在子線程中執(zhí)行照皆。 如果任務(wù) 4 是添加到另一個串行隊列或者并行隊列重绷,則任務(wù) 2 和任務(wù) 4 無序執(zhí)行(可以添加多個任務(wù)看效果)
performSelector
這個方法要創(chuàng)建提交任務(wù)到runloop上的,而gcd底層創(chuàng)建的線程是默認(rèn)沒有開啟對應(yīng)runloop的膜毁,所有的這個方法就會失效
如果改為主隊列昭卓,所在的主線程是默認(rèn)開啟runloop的
延時函數(shù)
dispatch-after能讓我們添加進(jìn)隊列的任務(wù)延時執(zhí)行愤钾,該函數(shù)并不是在指定時間后執(zhí)行處理,而只是在指定時間追加處理到dispatch-queue
使用dispatch-once實現(xiàn)單例
NSOperationQueue的有點
NSOperation候醒、NSOperationQueue是蘋果提供給我們的一套多線程解決方案绰垂,十幾上他們是基于GCD更高一層的封裝,完全面對對象火焰,但是比GCD更簡單易用劲装、代碼可讀性也更高
1,可以添加任務(wù)依賴昌简,方便控制執(zhí)行順序
2占业,可以設(shè)定操作執(zhí)行的優(yōu)先級
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栓袖,或者自定義子類來封裝操作匣摘。
操作隊列(operation queue):
這里的隊列指操作隊列,即用來存放操作的隊列裹刮。不同于 GCD 中的調(diào)度隊列 FIFO(先進(jìn)先出)的原則音榜。 NSOperationQueue 對于添加到隊列中的操作,首先進(jìn)入準(zhǔn)備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴 關(guān)系)捧弃,然后進(jìn)入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對的優(yōu)先級決定(優(yōu) 先級是操作對象自身的屬性)赠叼。
操作隊列通過設(shè)置最大并發(fā)操作數(shù)(maxConcurrentOperationCount)來控制并發(fā)、串行塔橡。 NSOperationQueue 為我們提供了兩種不同類型的隊列:主隊列和自定義隊列梅割。主隊列運(yùn)行在主線程之上霜第, 而自定義隊列在后臺執(zhí)行葛家。
自旋鎖與互斥鎖
自旋鎖: 是一種用于保護(hù)多線程共享資源的鎖,與一般互斥鎖(mutex)不同之處在于當(dāng)自旋鎖嘗試獲取鎖時以忙等 待(busy waiting)的形式不斷地循環(huán)檢查鎖是否可用泌类。當(dāng)上一個線程的任務(wù)沒有執(zhí)行完畢的時候(被鎖遵恕)底燎, 那么下一個線程會一直等待(不會睡眠),當(dāng)上一個線程的任務(wù)執(zhí)行完畢弹砚,下一個線程會立即執(zhí)行双仍。 在多 CPU 的環(huán)境中,對持有鎖較短的程序來說桌吃,使用自旋鎖代替一般的互斥鎖往往能夠提高程序的性能朱沃。
互斥鎖: 當(dāng)上一個線程的任務(wù)沒有執(zhí)行完畢的時候(被鎖住)茅诱,那么下一個線程會進(jìn)入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢逗物, 當(dāng)上一個線程的任務(wù)執(zhí)行完畢,下一個線程會自動喚醒然后執(zhí)行任務(wù)瑟俭。
總結(jié): 自旋鎖會忙等: 所謂忙等翎卓,即在訪問被鎖資源時,調(diào)用者線程不會休眠摆寄,而是不停循環(huán)在那里失暴,直到被鎖 資源釋放鎖。 互斥鎖會休眠: 所謂休眠微饥,即在訪問被鎖資源時逗扒,調(diào)用者線程會休眠,此時 cpu 可以調(diào)度其他線程工 作欠橘。直到被鎖資源釋放鎖缴阎。此時會喚醒休眠線程。
優(yōu)缺點:
自旋鎖的優(yōu)點在于简软,因為自旋鎖不會引起調(diào)用者睡眠蛮拔,所以不會進(jìn)行線程調(diào)度,CPU 時間片輪轉(zhuǎn)等耗時操 作痹升。所有如果能在很短的時間內(nèi)獲得鎖建炫,自旋鎖的效率遠(yuǎn)高于互斥鎖。 缺點在于疼蛾,自旋鎖一直占用 CPU肛跌,他在未獲得鎖的情況下,一直運(yùn)行--自旋察郁,所以占用著 CPU衍慎,如果不 能在很短的時 間內(nèi)獲得鎖,這無疑會使 CPU 效率降低皮钠。自旋鎖不能實現(xiàn)遞歸調(diào)用稳捆。 自旋鎖:atomic、OSSpinLock麦轰、dispatch_semaphore_t 互斥鎖:pthread_mutex乔夯、@ synchronized砖织、NSLock、NSConditionLock 末荐、NSCondition侧纯、NSRecursiveLock