進程:面試
· 1.進程是一個具備必定獨立功能的程序關於某次數據集合的一次運行活動,它是操做系統分配資源的基本單元.編程
· 2.進程是指在系統中正在運行的一個應用程序,就是一段程序的執行過程,咱們能夠理解爲手機上的一個 app
.安全
· 3.每一個進程之間是獨立的,每一個進程均運行在其專用且受保護的內存空間內,擁有獨立運行所需的所有資源markdown
線程網絡
· 1.程序執行流的最小單元,線程是進程中的一個實體.數據結構
· 2.一個進程要想執行任務,必須至少有一條線程.應用程序啓動的時候,系統會默認開啓一條線程,也就是主線程多線程
進程和線程的關係併發
· 1.線程是進程的執行單元,進程的全部任務都在線程中執行app
· 2.線程是CPU 分配資源和調度的最小單位異步
· 3.一個程序能夠對應多個進程(多進程),一個進程中可有多個線程,但至少要有一條線程
· 4.同一個進程內的線程共享進程資源
多進程
打開 mac
的活動監視器,能夠看到不少個進程同時運行
· 進程是程序在計算機上的一次執行活動。當你運行一個程序,你就啓動了一個進程。顯然,程序是死的(靜態的),進程是活的(動態的)。
· 進程能夠分爲系統進程和用戶進程。凡是用於完成操做系統的各類功能的進程就是系統進程,它們就是處於運行狀態下的操做系統自己;全部由用戶啓動的進程都是用戶進程。進程是操做系統進行資源分配的單位。
· 進程又被細化爲線程,也就是一個進程下有多個能獨立運行的更小的單位。在同一個時間裏,同一個計算機系統中若是容許兩個或兩個以上的進程處於運行狀態,這即是多進程。
多線程
1. 同一時間,CPU
只能處理 1 條線程,只有 1 條線程在執行。多線程併發執行,實際上是 CPU
快速地在多條線程之間調度(切換)。若是 CPU
調度線程的時間足夠快,就形成了多線程併發執行的假象
2. 若是線程很是很是多,CPU
會在 N 多線程之間調度,消耗大量的 CPU
資源,每條線程被調度執行的頻次會下降(線程的執行效率下降)
3. 多線程的優勢:
能適當提升程序的執行效率能適當提升資源利用率(CPU
、內存利用率)
開啓線程須要佔用必定的內存空間(默認狀況下,主線程佔用 1M,子線程佔用 512KB),若是開啓大量的線程,會佔用大量的內存空間,下降程序的性能線程越多,CPU
在調度線程上的開銷就越大
程序設計更加複雜:好比線程之間的通訊、多線程的數據共享
首先做爲一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個個人iOS開發公衆號:編程大鑫,無論你是小白仍是大牛都歡迎入駐 ,讓咱們一塊兒進步,共同發展!(羣內會免費提供一些羣主收藏的免費學習書籍資料以及整理好的幾百道面試題和答案文檔!)
任務
就是執行操做的意思,也就是在線程中執行的那段代碼。在GCD
中是放在 block
中的。執行任務有兩種方式:同步執行(sync
)和異步執行(async
)
同步(Sync
):同步添加任務到指定的隊列中,在添加的任務執行結束以前,會一直等待,直到隊列裏面的任務完成以後再繼續執行,即會阻塞線程。只能在當前線程中執行任務(是當前線程,不必定是主線程),
不具有開啓新線程的能力。
異步(Async
):線程會當即返回,無需等待就會繼續執行下面的任務,不阻塞當前線程。能夠在新的線程中執行任務,具有開啓新線程的能力(並不必定開啓新線程)。若是不是添加到主隊列上,異步會在子線程中執行任務
隊列
隊列(Dispatch Queue
):這裏的隊列指執行任務的等待隊列,即用來存聽任務的隊列。隊列是一種特殊的線性表,採用 FIFO
(先進先出)的原則,即新任務老是被插入到隊列的末尾,而讀取任務的時候老是從隊列的頭部開始讀取。每讀取一個任務,則從隊列中釋放一個任務在 GCD
中有兩種隊列:串行隊列和併發隊列。二者都符合FIFO
(先進先出)的原則。二者的主要區別是:執行順序不一樣,以及開啓線程數不一樣。
l 串行隊列(Serial Dispatch Queue
):
同一時間內,隊列中只能執行一個任務,只有當前的任務執行完成以後,才能執行下一個任務。(只開啓一個線程,一個任務執行完畢後,再執行下一個任務)。主隊列是主線程上的一個串行隊列,是系統自動爲咱們建立的
l 併發隊列(Concurrent Dispatch Queue
):
同時容許多個任務併發執行。(能夠開啓多個線程,而且同時執行任務)。併發隊列的併發功能只有在異步(dispatch_async
)函數下才有效
主要有三種:NSThread、NSoperationQueue、GCD
1. NSThread:輕量級別的多線程技術
是咱們本身手動開闢的子線程,若是使用的是初始化方式就須要咱們本身啓動,若是使用的是構造器方式它就會自動啓動。只要是咱們手動開闢的線程,都須要咱們本身管理該線程,不僅是啓動,還有該線程使用完畢後的資源回收
performSelector
...只要是 NSObject
的子類或者對象均可以經過調用方法進入子線程和主線程,其實這些方法所開闢的子線程也是 NSThread
的另外一種體現方式。
在編譯階段並不會去檢查方法是否有效存在,若是不存在只會給出警告
須要注意的是:若是是帶afterDelay
的延時函數,會在內部建立一個 NSTimer
,而後添加到當前線程的
Runloop
中。也就是若是當前線程沒有開啓runloop
,該方法會失效。在子線程中,須要啓動 runloop
(注
意調用順序)
而 performSelector:withObject
:只是一個單純的消息發送,和時間沒有一點關係。因此不須要添加到子
線程的 Runloop
中也能執行
二、GCD 對比 NSOprationQueue
咱們要明確 NSOperationQueue
與 GCD
之間的關係
GCD
是面向底層的 C 語言的 API
, NSOpertaionQueue
用 GCD
構建封裝的,是 GCD
的高級抽象。
一、 GCD
執行效率更高,並且因爲隊列中執行的是由 block
構成的任務,這是一個輕量級的數據結構,寫起來更方便
二、 GCD
只支持 FIFO
的隊列,而 NSOperationQueue
能夠經過設置最大併發數,設置優先級,添加依賴關係等調整執行順序
三、 NSOperationQueue
甚至能夠跨隊列設置依賴關係,可是 GCD
只能經過設置串行隊列,或者在隊列內添加 barrier(dispatch_barrier_async)
任務,才能控制執行順序,較爲複雜
四、 NSOperationQueue
由於面向對象,因此支持 KVO
,能夠監測 operation
是否正在執行( isExecuted
)、是否結束( isFinished
)、是否取消( isCanceld
)
l 實際項目開發中,不少時候只是會用到異步操做,不會有特別複雜的線程關係管理,因此蘋果推崇的且優化完善、運行快速的 GCD
是首選
l 若是考慮異步操做之間的事務性,順序行,依賴關係,好比多線程併發下載,GCD須要本身寫更多的代碼來實現,而 NSOperationQueue
已經內建了這些支持
l 不管是 GCD
仍是 NSOperationQueue
,咱們接觸的都是任務和隊列,都沒有直接接觸到線程,事實上線程管理也的確不須要咱們操心,系統對於線程的建立,調度管理和釋放都作得很好。而 NSThread
須要咱們本身去管理線程的生命週期,還要考慮線程同步、加鎖問題,形成一些性能上的開銷
iOS
中,有 GCD
、 NSOperation
、 NSThread
等幾種多線程技術方案。
而 GCD
共有三種隊列類型:
main queue
:經過 dispatch_get_main_queue
()得到,這是一個與主線程相關的串行隊列。
global queue
:全局隊列是併發隊列,由整個進程共享。存在着高、中、低三種優先級的全局隊列。調用 dispath_get_global_queue
並傳入優先級來訪問隊列。
自定義隊列:經過函數 dispatch_queue_create
建立的隊列
死鎖就是隊列引發的循環等待
一、一個比較常見的死鎖例子:主隊列同步
在主線程中運用主隊列同步,也就是把任務放到了主線程的隊列中。
同步對於任務是馬上執行的,那麼當把任務放進主隊列時,它就會立馬執行,只有執行完這個任務, viewDidLoad
纔會繼續向下執行。
而 viewDidLoad
和任務都是在主隊列上的,因爲隊列的先進先出原則,任務又需等待 viewDidLoad
執行完畢後才能繼續執行, viewDidLoad
和這個任務就造成了相互循環等待,就形成了死鎖。
想避免這種死鎖,能夠將同步改爲異步 dispatch_async
,或者將 dispatch_get_main_queue
換成其餘串行或並行隊列,均可以解決。
二、一樣,下邊的代碼也會形成死鎖:
外面的函數不管是同步仍是異步都會形成死鎖。
這是由於裏面的任務和外面的任務都在同一個 serialQueue
隊列內,又是同步,這就和上邊主隊列同步的例子同樣形成了死鎖解決方法也和上邊同樣,將裏面的同步改爲異步 dispatch_async
,或者將 serialQueue
換成其餘串行或並行隊列,均可以解決
這樣是不會死鎖的,而且 serialQueue
和 serialQueue2
是在同一個線程中的。
一、串行隊列先異步後同步
打印順序是 13245
緣由是:
首先先打印 1
接下來將任務 2 其添加至串行隊列上,因爲任務 2 是異步,不會阻塞線程,繼續向下執行,打印 3
而後是任務 4,將任務 4 添加至串行隊列上,由於任務 4 和任務 2 在同一串行隊列,根據隊列先進先出原則,任務 4 必須等任務 2 執行後才能執行,又由於任務 4 是同步任務,會阻塞線程,只有執行完任務 4 才能繼續向下執行打印 5
因此最終順序就是 13245。
這裏的任務 4 在主線程中執行,而任務 2 在子線程中執行。
若是任務 4 是添加到另外一個串行隊列或者並行隊列,則任務 2 和任務 4 無序執行(能夠添加多個任務看效果)
二、performSelector
這裏的 test
方法是不會去執行的,緣由在於
這個方法要建立提交任務到runloop
上的,而 gcd
底層建立的線程是默認沒有開啓對應runloop
的,全部這個方法就會失效。
而若是將 dispatch_get_global_queue
改爲主隊列,因爲主隊列所在的主線程是默認開啓了runloop
的,就會去執行(將 dispatch_async
改爲同步,由於同步是在當前線程執行,那麼若是當前線程是主線程,test
方法也是會去執行的)。
一、問:怎麼用 GCD 實現多讀單寫?
多讀單寫的意思就是:能夠多個讀者同時讀取數據,而在讀的時候,不能取寫入數據。而且,在寫的過程當中,不能有其餘寫者去寫。即讀者之間是併發的,寫者與讀者或其餘寫者是互斥的。
這裏的寫處理就是經過柵欄的形式去寫。
就能夠用 dispatch_barrier_sync
(柵欄函數)去實現
二、dispatch_barrier_sync 的用法:
這裏的dispatch_barrier_sync
上的隊列要和須要阻塞的任務在同一隊列上,不然是無效的。
從打印上看,任務 0-9 和任務任務 10-19 由於是異步併發的緣由,彼此是無序的。而因爲柵欄函數的存在,致使順序必然是先執行任務 0-9,再執行柵欄函數,再去執行任務 10-19。
l dispatch_barrier_sync: Submits a barrier block object for execution and waits until that block completes.
(提交一個柵欄函數在執行中,它會等待柵欄函數執行完)
l dispatch_barrier_async: Submits a barrier block for asynchronous execution and returns immediately.
(提交一個柵欄函數在異步執行中,它會立馬返回)
而dispatch_barrier_sync
和dispatch_barrier_async
的區別也就在於會不會阻塞當前線程好比,上述代碼若是在 dispatch_barrier_async
後隨便加一條打印,則會先去執行該打印,再去執行任務 0-9 和柵欄函數;而若是是 dispatch_barrier_sync
,則會在任務 0-9 和柵欄函數後去執行這條打印。
三、則能夠這樣設計多讀單寫:
場景:在 n 個耗時併發任務都完成後,再去執行接下來的任務。好比,在 n 個網絡請求完成後去刷新 UI 頁
面。
GCD
中的信號量是指 Dispatch Semaphore
,是持有計數的信號。
Dispatch Semaphore
提供了三個函數
1.dispatch_semaphore_create
:建立一個Semaphore
並初始化信號的總量
2.dispatch_semaphore_signal
:發送一個信號,讓信號總量加1
3.dispatch_semaphore_wait
:可使總信號量減1,當信號總量爲0時就會一直等待(阻塞所在線程),不然就能夠正常執行。
Dispatch Semaphore
在實際開發中主要用於:
l 保持線程同步,將異步執行任務轉換爲同步執行任務
l 保證線程安全,爲線程加鎖
一、保持線程同步:
dispatch_semaphore_wait
加鎖阻塞了當前線程,dispatch_semaphore_signal
解鎖後當前線程繼續執行
二、保證線程安全,爲線程加鎖:
在線程安全中能夠將dispatch_semaphore_wait
看做加鎖,而 dispatch_semaphore_signal
看做解鎖首先建立全局變量
注意到這裏的初始化信號量是 1。
異步併發調用asyncTask
而後發現打印是從任務 1順序執行到 100,沒有發生兩個任務同時執行的狀況。
緣由以下:
在子線程中併發執行 asyncTask
,那麼第一個添加到併發隊列裏的,會將信號量減 1,此時信號量等於 0,
能夠執行接下來的任務。而併發隊列中其餘任務,因爲此時信號量不等於 0,必須等當前正在執行的任務執行完畢後調用 dispatch_semaphore_signal
將信號量加 1,才能夠繼續執行接下來的任務,以此類推,從而達到線程加鎖的目的。
dispatch_after
能讓咱們添加進隊列的任務延時執行,該函數並非在指定時間後執行處理,而只是在指定時間追加處理到 dispatch_queue
因爲其內部使用的是dispatch_time_t
管理時間,而不是 NSTimer
。
因此若是在子線程中調用,相比 performSelector:afterDelay
,不用關心 runloop
是否開啓
NSOperation
、NSOperationQueue
是蘋果提供給咱們的一套多線程解決方案。實際上 NSOperation
、NSOperationQueue
是基於GCD
更高一層的封裝,徹底面向對象。可是比GCD
更簡單易用、代碼可讀性也更高。
一、能夠添加任務依賴,方便控制執行順序
二、能夠設定操做執行的優先級
三、任務執行狀態控制:isReady
,isExecuting
,isFinished
,isCancelled
若是隻是重寫 NSOperation
的 main
方法,由底層控制變動任務執行及完成狀態,以及任務退出若是重寫了 NSOperation
的 start
方法,自行控制任務狀態
系統經過KVO
的方式移除 isFinished
==YES
的 NSOperation
四、能夠設置最大併發量
#十4、NSOperation 和 NSOperationQueue
操做(Operation):
執行操做的意思,換句話說就是你在線程中執行的那段代碼。
在 GCD
中是放在block
中的。在NSOperation
中,使用 NSOperation
子類 NSInvocationOperation
、NSBlockOperation
,或者自定義子類來封裝操做。
操做隊列(Operation Queues):
這裏的隊列指操做隊列,即用來存放操做的隊列。不一樣於GCD
中的調度隊列FIFO
(先進先出)的原則。
NSOperationQueue
對於添加到隊列中的操做,首先進入準備就緒的狀態(就緒狀態取決於操做之間的依賴
關係),而後進入就緒狀態的操做的開始執行順序(非結束執行順序)由操做之間相對的優先級決定(優先級是操做對象自身的屬性)。
操做隊列經過設置最大併發操做數(maxConcurrentOperationCount
)來控制併發、串行。
NSOperationQueue
爲咱們提供了兩種不一樣類型的隊列:主隊列和自定義隊列。主隊列運行在主線程之上,
而自定義隊列在後臺執行。
NSThread
在實際開發中比較經常使用到的場景就是去實現常駐線程。
因爲每次開闢子線程都會消耗cpu
,在須要頻繁使用子線程的狀況下,頻繁開闢子線程會消耗大量的cpu
,並且建立線程都是任務執行完成以後也就釋放了,不能再次利用,那麼如何建立一個線程可讓它能夠再次工做呢?也就是建立一個常駐線程。
首先常駐線程既然是常駐,那麼咱們能夠用GCD
實現一個單例來保存 NSThread
這樣建立的thread
就不會銷燬了嗎?
並無打印,說明 test
方法沒有被調用。那麼能夠用 runloop
來讓線程常駐
這時候再去調用performSelector
就有打印了。
自旋鎖:
是一種用於保護多線程共享資源的鎖,與通常互斥鎖(mutex
)不一樣之處在於當自旋鎖嘗試獲取鎖時以忙等待(busy waiting
)的形式不斷地循環檢查鎖是否可用。當上一個線程的任務沒有執行完畢的時候(被鎖住),那麼下一個線程會一直等待(不會睡眠),當上一個線程的任務執行完畢,下一個線程會當即執行。
在多 CPU
的環境中,對持有鎖較短的程序來講,使用自旋鎖代替通常的互斥鎖每每可以提升程序的性能。
互斥鎖:
當上一個線程的任務沒有執行完畢的時候(被鎖住),那麼下一個線程會進入睡眠狀態等待任務執行完畢,當上一個線程的任務執行完畢,下一個線程會自動喚醒而後執行任務。
總結:
自旋鎖會忙等: 所謂忙等,即在訪問被鎖資源時,調用者線程不會休眠,而是不停循環在那裏,直到被鎖資源釋放鎖。
互斥鎖會休眠:所謂休眠,即在訪問被鎖資源時,調用者線程會休眠,此時 cpu
能夠調度其餘線程工做。直到被鎖資源釋放鎖。此時會喚醒休眠線程。
優缺點:
自旋鎖的優勢在於,由於自旋鎖不會引發調用者睡眠,因此不會進行線程調度,CPU
時間片輪轉等耗時操做。全部若是能在很短的時間內得到鎖,自旋鎖的效率遠高於互斥鎖。
缺點在於,自旋鎖一直佔用 CPU
,他在未得到鎖的狀況下,一直運行--自旋,因此佔用着 CPU
,若是不能在很短的時 間內得到鎖,這無疑會使 CPU
效率下降。自旋鎖不能實現遞歸調用。
自旋鎖:atomic
、OSSpinLock
、dispatch_semaphore_t
互斥鎖:pthread_mutex
、@ synchronized
、NSLock
、NSConditionLock
、NSCondition
、NSRecursiveLock
好了,以上就是本次內容,感謝觀看!