GCD NSOperation 執行緒安全

weixin_33872660發表於2016-09-06

"GCD:充分利用裝置的多核(自動)他的生命週期是自動管理"

基本知識:GCD是通過"佇列"和"任務"實現多執行緒程式設計

佇列:1,全域性併發佇列。2,主佇列:凡是新增到主佇列中的任務一律放在主執行緒中執行。3,自己建立的併發佇列。4,自己建立的序列佇列。

任務:1,同步。2,非同步。

GCD的使用:佇列+任務(函式)

1,非同步函式+併發佇列:開啟多條執行緒,併發執行任務。

2,非同步函式+序列佇列:開啟一條執行緒,序列執行任務。

3,非同步函式+主佇列:不開執行緒,序列執行任務。

4,同步函式+併發佇列:不開執行緒,序列執行任務。

5,同步函式+序列佇列:不開執行緒,序列執行任務。

"6,同步函式+主佇列:不開執行緒,序列執行任務,如果函式程式碼寫在主執行緒的中會發生死鎖現象。"(這種使用的場景,執行這個函式的執行緒不是主執行緒,並且函式內部的任務依賴於"執行函式的任務"執行完畢之後才執行的情況)

延伸:為什麼會發生死鎖現象?

如果把同步函式+主佇列的程式碼寫到了主執行緒要執行的程式碼裡面,那麼執行這個函式就是主執行緒的一個任務,當程式碼執行到函式內部的任務的時候,因為主佇列的任務一律新增到主執行緒中執行的特點,這個任務要等待主執行緒的其他任務執行完畢才會執行,因為主執行緒當前正在執行當前這個函式,主執行緒此時並沒有執行完畢,所以函式內部任務現在不會執行,這個任務不執行,那麼這個函式永遠不會執行完畢,執行函式的這個任務在等待函式內部任務的執行完畢,而函式內部的任務在等待執行這個函式的任務執行完畢,互相等待,所以就會出現死鎖現象。

GCD佇列的建立和釋放:iOS6.0之前,使用帶creat的函式之後,都要進行一次release操作,iOS6.0之後把GCD納入了ARC的記憶體管理機制裡面。

GCD其他常用函式:

1,柵欄函式:柵欄函式可以用來控制任務的執行順序。用來分割,函式上免得所有任務執行完之後再執行柵欄函式後面的任務。注意:不能使用全域性併發佇列。

2,一次性函式:在整個程式執行過程中,一次性函式裡面的程式碼只會執行一次。注意:不能把一次性程式碼放到懶載入中。

3,延遲執行:dispatch_after

4,快速迭代:就是開多個執行緒併發的執行所有任務。和for迴圈的區別就是for迴圈是在同一個執行緒中執行的。注意:使用快速迭代的時候不能使用主佇列。

5,佇列組:dispatch_group_notify函式是等佇列組裡的所有佇列都執行完畢時最後再執行。


NSOperation:

" NSOperation基於GCD而封裝的,比GCD多了一些更簡單實用的功能,使用起來更加的物件導向。生命週期是自動管理"

NSOperation用來封裝操作,NSOperationQueue用來存放任務。步驟:封裝操作,把操作新增到佇列。

子類NSInvocationOperation:

封裝操作:NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(download1) object:nil];

執行操作:[op1 start];

子類NSBlockOperation :

封裝操作:NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"1--%@",[NSThread currentThread]);

}];

新增操作:[op1 addExecutionBlock:^{

NSLog(@"4++++%@",[NSThread currentThread]);

}];

執行操作:[op1 start];

設定依賴:[操作1addDependency操作2];(依賴,你執行完了我在執行)可以跨佇列依賴,不可以迴圈依賴。

注意:一個block就相當於一個任務,當一個操作中的任務數量大於一的時候就會開啟子執行緒,和當前執行緒同時執行任務。

自定義NSOperation:可以重寫main方法,把耗時操作寫在main方法裡面。優點:遮蔽了操作,方便程式碼的封裝和複用。

start方法也是呼叫了main方法,蘋果官方建議,沒執行完一段耗時操作之後就檢查當前操作是否被取消。佇列取消了是不可恢復的。

NSOperationQueue操作佇列,用來存放任務。1,分為主佇列和非主佇列,凡是新增到主佇列中的任務都是在主執行緒中執行。2,非主佇列同時具備了序列和併發的功能,預設就是併發佇列。

基本使用:建立一個操作佇列,按照上面操作的封裝方法封裝操作之後新增到佇列,這個新增的方法內部會實現start方法。

可以設定最大併發數,最大併發數設為1,整個佇列就是序列佇列了,預設值是-1,-1就代表併發佇列。

佇列的掛起:YES是暫停。NO是繼續。


"GCD和NSOperation"的區別:

GCD是C語言的API,而操作佇列是OC的物件,GCD中任務快,是個輕量級的資料結構,而操作佇列的NSOperation是個更加重量級的OC物件。

NSOperation和NSOperationQueue的好處:操作佇列可以方便的呼叫cancel來取消某個操作,GCD中是無法取消的,安排好任務就不管了。

NSOperation可以方便的指定操作間的依賴關係。

NSOperation可以通過KVO提供對NSOperation物件的精細控制。

NSOperation可以方便的指定操作的優先順序。

通過自定義NSOperation的子類可以實現操作的重用。

"執行緒安全"

1,執行緒的幾種狀態:新建狀態、就緒狀態、執行狀態、阻塞狀態、死亡狀態。

2,執行緒死亡之後不能重新開啟。

3,多個執行緒溶蝕訪問一塊資源就會發生資料安全問題。解決方法:加互斥鎖。(同步鎖,就是讓執行緒以同步的形式執行)

加互斥鎖注意點:1,必須使用同一把鎖。2,建議使用self。3,注意加鎖的位置。4,加鎖耗費效能,不要亂加。

原子(atomic)和非原子屬性(noatomic):atomic會對setter方法加鎖。noatomic不會對setter加鎖。在宣告屬性的時候不寫的話預設就是加鎖的atomic,屬性多了而且都加了鎖,特別耗費效能。所以建議在宣告屬性的時候使用noatomic,提高效能。

執行迴圈:

"Runloop"用來保持程式的持續執行,處理APP中的各種事件(比如觸控事件、定時器事件、selector事件),節省CPU資源,提高程式效能,有事情就做事情,沒事情就休息。

Runloop與執行緒的關係:一個Runloop對應著一條唯一的執行緒。主執行緒的Runloop已經建立好了,子執行緒的runloop需要手動建立(currentRunloop)。

宣告週期:Runloop在第一次獲取時建立,線上程結束時銷燬。

Runloop相關的5個類

CFRunloopRef(Runloop物件)

CFRunloopModeRef(Runloop的執行模式)

CFRunloopSourceRef(Runloop要處理的事件源)

CFRunloopTimerRef(Timer事件)

CFRunloopObserverRef(Runloop的觀察者(監聽者))

相關文章