首先每個正在運行的應用程序都是一個進程,每個進程系統(tǒng)都會分配獨立的內(nèi)存資源。而一個進程中的所有任務都是在線程中執(zhí)行的,所以每個進程至少有一個線程,這個線程也稱為主線程,而如果想要并發(fā)執(zhí)行多個任務那么就要開啟多條線程,也稱為多線程。多線程在一定意義上實現(xiàn)了進程內(nèi)的資源共享,以及效率的提升。同時,在一定程度上相對獨立,是執(zhí)行任務最基本的單元,有自己棧和寄存器。而對應單核 CPU 來說,多線程并不是真正意義上并發(fā)執(zhí)行任務,只是CPU快速地在多條線程之間調(diào)度,CPU調(diào)度線程的時間足夠短,就造成了多線程并發(fā)執(zhí)行的假象。就單核CPU而言多線程可以解決線程阻塞的問題,但是其本身效率并沒有提高,多核CPU的并行才真正解決了運行效率問題。
iOS 多線程方案
1. pthread 這個是一套 C 語言跨平臺多線程 API,其產(chǎn)生的線程生命周期是需要手動管理
2. NSThread 是蘋果對 pthread 面向?qū)ο蟮姆庋b,和上面一樣是需要手動管理線程生命周期
3. GCD 是蘋果另一套多線程解決方案,其線程生命周期是自動管理,使用簡單,也是最常使用的
4. NSOperation 是蘋果基于 GCD 面向?qū)ο蠓庋b,提供了一實用的功能,例如設置線程最大并發(fā)數(shù),添加線程間依賴等
GCD
首先在 GCD 中并不直接操作線程,而是通過其提供的任務管理方式隊列和任務執(zhí)行方式同步和異步,間接管理線程
隊列作用:任務的執(zhí)行方式
隊列分為兩種,一種是是串行隊列,一種是并發(fā)隊列
串行隊列:一個任務完成后再執(zhí)行下一個任務,根據(jù)先進先出的按順序執(zhí)行
并發(fā)隊列:多個任務并發(fā)(同時)執(zhí)行
特殊的隊列:全局隊列和主隊列
全局隊列是并發(fā)隊列,而主隊列是在主線程上,是串行隊列
同步和異步作用:能不能開啟新的線程
同步:在當前線程中執(zhí)行任務,不具備開啟新線程的能力
異步:在新的線程中執(zhí)行任務,具備開啟新線程的能力
只要是同步或主隊列兩者占一項,就不會開啟新線程,串行執(zhí)行任務;
只有異步并且不是主隊列,才會開啟新線程,根據(jù)其搭配的隊列并發(fā)或串行是否執(zhí)行任務
在 GCD 中使用 sync 同步函數(shù)往當前串行隊列添加任務,就會產(chǎn)生死鎖阻塞線程
常用函數(shù)?
dispatch_sync 同步執(zhí)行任務
dispatch_async 異步執(zhí)行任務
dispatch_once 只執(zhí)行一次使當前添加的任務,并且是線程安全的,常用于創(chuàng)建單例對象
dispatch_apply 使當前添加的任務執(zhí)行指定次數(shù),這個相當于內(nèi)置循環(huán)執(zhí)行多次任務,這個函數(shù)會調(diào)用主線程執(zhí)行任務,如果是任務耗時不要使用,使用手動設置循環(huán)調(diào)用多次異步并發(fā)隊列執(zhí)行耗時任務
dispatch_after 使任務延遲執(zhí)行
dispatch_group_async 與?dispatch_group_notify 添加任務依賴,執(zhí)行完前者的任務之后都會執(zhí)行后者的任務
dispatch_group_wait 會阻塞當前線程,等待 group 中的任務執(zhí)行完,之后再回到當前線程執(zhí)行
dispatch_barrier_async 并發(fā)執(zhí)行任務時,當前添加任務不會執(zhí)行,執(zhí)行當前任務時是串行執(zhí)行,使用場景 IO的多讀單寫
線程同步方案
OSSpinLock
這個是自旋鎖,等待鎖的線程會處于忙等狀態(tài),一直占用 CPU 資源,從 iOS10開始,會出現(xiàn)警告,因為這個鎖不再安全,可能會出現(xiàn)優(yōu)先級反轉(zhuǎn)的問題,如果等待的線程優(yōu)先級較高,它會一直占用?CPU 資源,優(yōu)先級低的線程就無法釋放鎖資源
os_unfair_lock
是 iOS10之后才支持的,是 apple 用來取代 OSSpinLock 一種線程同步方案,是互斥鎖,和自旋鎖區(qū)別在于等待鎖資源的線程會處于休眠狀態(tài),并非忙等狀態(tài)
一般開啟多線程時,如果涉及到線程同步的說,不建議修改線程優(yōu)先級,容易出現(xiàn)優(yōu)先級反轉(zhuǎn)的問題,特別是使用 OSSpinLock 時
一般來說,只要不是對耗時任務進行使用線程同步,那么使用自旋鎖的效率和開銷會優(yōu)于互斥鎖,因為線程執(zhí)行任務的時間短,自旋鎖會一直忙等,及時拿到鎖資源,如果是互斥鎖,因為線程執(zhí)行任務的時間短,這種頻繁休眠喚醒操作會增加資源開銷,降低效率,如果是耗時任務線程同步,那么使用互斥鎖優(yōu)于自旋鎖,因為自旋鎖等待的線程處于激活狀態(tài)一直訪問鎖資源,而另外拿到鎖資源的線程此時還沒給 CPU 調(diào)度到,所以此時耗時任務沒有執(zhí)行完,無法釋放鎖資源,此時會降低效率
兩者 api 幾乎一樣,鎖的初始化都是宏定義提供的,前者宏定義是0,后者是將 os_unfair_lock 結(jié)構(gòu)體初始化
#define OS_SPINLOCK_INIT? ? 0
#define OS_UNFAIR_LOCK_INIT ((os_unfair_lock){0})
提供的3個函數(shù) lock unlock tryLock
tryLock 表示,當前是否能拿到鎖資源,能就返回 YES,可以配合 if 一起使用
pthread_mutex
mutex 表示互斥鎖,是一套 c 語言的跨平臺線程同步 api
初始化是,可以選擇鎖的類型,一種是默認就是互斥鎖,一種是遞歸鎖
遞歸鎖的特性是,不同線程間就跟互斥鎖特性一樣,但是可以讓同一個線程多次獲取鎖資源,主要使用場景就是對一個遞歸方法進行線程同步
mutex 這套 api 還有個特點就是給線程添加條件 condition
他提供了三個函數(shù)
pthread_cond_wait() 接收兩個參數(shù),一個是條件,一個線程,可以讓當前線程等待,因為是互斥鎖 ,等待時會休眠,同時釋放鎖的資源,等待被當前條件重新激活,激活時會喚醒,線程重新拿到鎖的資源
pthread_cond_signal() 會激活一個等待該條件的線程
pthread_cond_broadcast() 激活所有等待該條件的線程
NSLock
是 apple 對?mutex 互斥鎖的面向?qū)ο蠓庋b?
NSRecursiveLock
是 apple 對 mutex?遞歸鎖的面向?qū)ο蠓庋b?
NSCondition
是 apple 對?mutex 條件的面向?qū)ο蠓庋b?
NSConditionLock
這個 NSCondition 進一步的封裝,這點在于可以設置具體的條件值,只有滿足條件值的線程才能拿到鎖的資源,在釋放鎖的資源時可以更改條件值
@synchronized
也是對 mutex 遞歸鎖封裝,@synchronized(obj) 其內(nèi)部會生成 obj 對應的遞歸鎖,然后進行加鎖、解鎖操作,但是性能不好,不推薦使用
GCD 提供線程同步方案
dispatch_semaphore
semaphore 也就是信號量,這個信息量可以用來控制線程并發(fā)訪問量大數(shù)量
當設置信號量為1,代表同時只允許1條線程訪問資源,保證線程同步
提供 3個函數(shù)
dispatch_semaphore_create(value) 初始化信息號
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 如果信息量的值 <=0,當前線程就會進入休眠等待,直到信息量的值 >0,如果信息量的值 >0 就減 1,讓線程往下執(zhí)行后面的代碼
dispatch_semaphore_signal(semaphore) 讓信息號加1
wait 和 signal 和之前 condition 條件很像
DISPATCH_QUEUE_SERIAL
直接使用 GCD 的串行隊列,也是可以實現(xiàn)線程同步的