首先每個(gè)正在運(yùn)行的應(yīng)用程序都是一個(gè)進(jìn)程恭应,每個(gè)進(jìn)程系統(tǒng)都會分配獨(dú)立的內(nèi)存資源抄邀。而一個(gè)進(jìn)程中的所有任務(wù)都是在線程中執(zhí)行的,所以每個(gè)進(jìn)程至少有一個(gè)線程昼榛,這個(gè)線程也稱為主線程境肾,而如果想要并發(fā)執(zhí)行多個(gè)任務(wù)那么就要開啟多條線程剔难,也稱為多線程。多線程在一定意義上實(shí)現(xiàn)了進(jìn)程內(nèi)的資源共享奥喻,以及效率的提升偶宫。同時(shí),在一定程度上相對獨(dú)立环鲤,是執(zhí)行任務(wù)最基本的單元纯趋,有自己棧和寄存器。而對應(yīng)單核 CPU 來說冷离,多線程并不是真正意義上并發(fā)執(zhí)行任務(wù)吵冒,只是CPU快速地在多條線程之間調(diào)度,CPU調(diào)度線程的時(shí)間足夠短西剥,就造成了多線程并發(fā)執(zhí)行的假象痹栖。就單核CPU而言多線程可以解決線程阻塞的問題,但是其本身效率并沒有提高瞭空,多核CPU的并行才真正解決了運(yùn)行效率問題揪阿。
iOS 多線程方案
1. pthread 這個(gè)是一套 C 語言跨平臺多線程 API咆畏,其產(chǎn)生的線程生命周期是需要手動(dòng)管理
2. NSThread 是蘋果對 pthread 面向?qū)ο蟮姆庋b南捂,和上面一樣是需要手動(dòng)管理線程生命周期
3. GCD 是蘋果另一套多線程解決方案,其線程生命周期是自動(dòng)管理鳖眼,使用簡單黑毅,也是最常使用的
4. NSOperation 是蘋果基于 GCD 面向?qū)ο蠓庋b,提供了一實(shí)用的功能钦讳,例如設(shè)置線程最大并發(fā)數(shù)矿瘦,添加線程間依賴等
GCD
首先在 GCD 中并不直接操作線程,而是通過其提供的任務(wù)管理方式隊(duì)列和任務(wù)執(zhí)行方式同步和異步愿卒,間接管理線程
隊(duì)列作用:任務(wù)的執(zhí)行方式
隊(duì)列分為兩種缚去,一種是是串行隊(duì)列,一種是并發(fā)隊(duì)列
串行隊(duì)列:一個(gè)任務(wù)完成后再執(zhí)行下一個(gè)任務(wù)琼开,根據(jù)先進(jìn)先出的按順序執(zhí)行
并發(fā)隊(duì)列:多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
特殊的隊(duì)列:全局隊(duì)列和主隊(duì)列
全局隊(duì)列是并發(fā)隊(duì)列易结,而主隊(duì)列是在主線程上,是串行隊(duì)列
同步和異步作用:能不能開啟新的線程
同步:在當(dāng)前線程中執(zhí)行任務(wù)柜候,不具備開啟新線程的能力
異步:在新的線程中執(zhí)行任務(wù)搞动,具備開啟新線程的能力
只要是同步或主隊(duì)列兩者占一項(xiàng),就不會開啟新線程渣刷,串行執(zhí)行任務(wù)鹦肿;
只有異步并且不是主隊(duì)列,才會開啟新線程辅柴,根據(jù)其搭配的隊(duì)列并發(fā)或串行是否執(zhí)行任務(wù)
在 GCD 中使用 sync 同步函數(shù)往當(dāng)前串行隊(duì)列添加任務(wù)箩溃,就會產(chǎn)生死鎖阻塞線程
常用函數(shù)?
dispatch_sync 同步執(zhí)行任務(wù)
dispatch_async 異步執(zhí)行任務(wù)
dispatch_once 只執(zhí)行一次使當(dāng)前添加的任務(wù)瞭吃,并且是線程安全的,常用于創(chuàng)建單例對象
dispatch_apply 使當(dāng)前添加的任務(wù)執(zhí)行指定次數(shù)涣旨,這個(gè)相當(dāng)于內(nèi)置循環(huán)執(zhí)行多次任務(wù)歪架,這個(gè)函數(shù)會調(diào)用主線程執(zhí)行任務(wù),如果是任務(wù)耗時(shí)不要使用霹陡,使用手動(dòng)設(shè)置循環(huán)調(diào)用多次異步并發(fā)隊(duì)列執(zhí)行耗時(shí)任務(wù)
dispatch_after 使任務(wù)延遲執(zhí)行
dispatch_group_async 與?dispatch_group_notify 添加任務(wù)依賴和蚪,執(zhí)行完前者的任務(wù)之后都會執(zhí)行后者的任務(wù)
dispatch_group_wait 會阻塞當(dāng)前線程,等待 group 中的任務(wù)執(zhí)行完烹棉,之后再回到當(dāng)前線程執(zhí)行
dispatch_barrier_async 并發(fā)執(zhí)行任務(wù)時(shí)惠呼,當(dāng)前添加任務(wù)不會執(zhí)行,執(zhí)行當(dāng)前任務(wù)時(shí)是串行執(zhí)行峦耘,使用場景 IO的多讀單寫
線程同步方案
OSSpinLock
這個(gè)是自旋鎖,等待鎖的線程會處于忙等狀態(tài)旅薄,一直占用 CPU 資源辅髓,從 iOS10開始,會出現(xiàn)警告少梁,因?yàn)檫@個(gè)鎖不再安全洛口,可能會出現(xiàn)優(yōu)先級反轉(zhuǎn)的問題,如果等待的線程優(yōu)先級較高凯沪,它會一直占用?CPU 資源第焰,優(yōu)先級低的線程就無法釋放鎖資源
os_unfair_lock
是 iOS10之后才支持的,是 apple 用來取代 OSSpinLock 一種線程同步方案妨马,是互斥鎖挺举,和自旋鎖區(qū)別在于等待鎖資源的線程會處于休眠狀態(tài),并非忙等狀態(tài)
一般開啟多線程時(shí)烘跺,如果涉及到線程同步的說湘纵,不建議修改線程優(yōu)先級,容易出現(xiàn)優(yōu)先級反轉(zhuǎn)的問題滤淳,特別是使用 OSSpinLock 時(shí)
一般來說梧喷,只要不是對耗時(shí)任務(wù)進(jìn)行使用線程同步,那么使用自旋鎖的效率和開銷會優(yōu)于互斥鎖脖咐,因?yàn)榫€程執(zhí)行任務(wù)的時(shí)間短铺敌,自旋鎖會一直忙等,及時(shí)拿到鎖資源屁擅,如果是互斥鎖偿凭,因?yàn)榫€程執(zhí)行任務(wù)的時(shí)間短,這種頻繁休眠喚醒操作會增加資源開銷煤蹭,降低效率笔喉,如果是耗時(shí)任務(wù)線程同步取视,那么使用互斥鎖優(yōu)于自旋鎖,因?yàn)樽孕i等待的線程處于激活狀態(tài)一直訪問鎖資源常挚,而另外拿到鎖資源的線程此時(shí)還沒給 CPU 調(diào)度到作谭,所以此時(shí)耗時(shí)任務(wù)沒有執(zhí)行完,無法釋放鎖資源奄毡,此時(shí)會降低效率
兩者 api 幾乎一樣折欠,鎖的初始化都是宏定義提供的,前者宏定義是0吼过,后者是將 os_unfair_lock 結(jié)構(gòu)體初始化
#define OS_SPINLOCK_INIT? ? 0
#define OS_UNFAIR_LOCK_INIT ((os_unfair_lock){0})
提供的3個(gè)函數(shù) lock unlock tryLock
tryLock 表示锐秦,當(dāng)前是否能拿到鎖資源,能就返回 YES盗忱,可以配合 if 一起使用
pthread_mutex
mutex 表示互斥鎖酱床,是一套 c 語言的跨平臺線程同步 api
初始化是,可以選擇鎖的類型趟佃,一種是默認(rèn)就是互斥鎖扇谣,一種是遞歸鎖
遞歸鎖的特性是,不同線程間就跟互斥鎖特性一樣闲昭,但是可以讓同一個(gè)線程多次獲取鎖資源罐寨,主要使用場景就是對一個(gè)遞歸方法進(jìn)行線程同步
mutex 這套 api 還有個(gè)特點(diǎn)就是給線程添加條件 condition
他提供了三個(gè)函數(shù)
pthread_cond_wait() 接收兩個(gè)參數(shù),一個(gè)是條件序矩,一個(gè)線程鸯绿,可以讓當(dāng)前線程等待,因?yàn)槭腔コ怄i 簸淀,等待時(shí)會休眠瓶蝴,同時(shí)釋放鎖的資源,等待被當(dāng)前條件重新激活租幕,激活時(shí)會喚醒囊蓝,線程重新拿到鎖的資源
pthread_cond_signal() 會激活一個(gè)等待該條件的線程
pthread_cond_broadcast() 激活所有等待該條件的線程
NSLock
是 apple 對?mutex 互斥鎖的面向?qū)ο蠓庋b?
NSRecursiveLock
是 apple 對 mutex?遞歸鎖的面向?qū)ο蠓庋b?
NSCondition
是 apple 對?mutex 條件的面向?qū)ο蠓庋b?
NSConditionLock
這個(gè) NSCondition 進(jìn)一步的封裝,這點(diǎn)在于可以設(shè)置具體的條件值令蛉,只有滿足條件值的線程才能拿到鎖的資源聚霜,在釋放鎖的資源時(shí)可以更改條件值
@synchronized
也是對 mutex 遞歸鎖封裝,@synchronized(obj) 其內(nèi)部會生成 obj 對應(yīng)的遞歸鎖珠叔,然后進(jìn)行加鎖蝎宇、解鎖操作,但是性能不好祷安,不推薦使用
GCD 提供線程同步方案
dispatch_semaphore
semaphore 也就是信號量姥芥,這個(gè)信息量可以用來控制線程并發(fā)訪問量大數(shù)量
當(dāng)設(shè)置信號量為1,代表同時(shí)只允許1條線程訪問資源汇鞭,保證線程同步
提供 3個(gè)函數(shù)
dispatch_semaphore_create(value) 初始化信息號
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 如果信息量的值 <=0凉唐,當(dāng)前線程就會進(jìn)入休眠等待庸追,直到信息量的值 >0,如果信息量的值 >0 就減 1台囱,讓線程往下執(zhí)行后面的代碼
dispatch_semaphore_signal(semaphore) 讓信息號加1
wait 和 signal 和之前 condition 條件很像
DISPATCH_QUEUE_SERIAL
直接使用 GCD 的串行隊(duì)列淡溯,也是可以實(shí)現(xiàn)線程同步的