首先來了解什麼是多線程,進程和線程的區別.程序員
進程:編程
正在進行中的程序被稱爲進程,負責程序運行的內存分配;後端
每個進程都有本身獨立的虛擬內存空間.安全
線程:(主線程最大佔1M的棧區空間,每條子線程最大佔512K的棧區空間)網絡
線程是進程中一個獨立的執行路徑(控制單元);多線程
一個進程中至少包含一條線程,即主線程;併發
能夠將耗時的執行路徑(如網絡請求)放在其餘線程中執行;框架
線程不能被殺掉,可是能夠暫停/休眠一條線程.異步
建立線程的目的:async
開啓一條新的執行路徑,運行指定的代碼,與主線程中的代碼實現同時運行.
多任務調度系統:
每一個應用程序由操做系統分配的短暫的時間片(Timeslice)輪流使用CPU,因爲CPU對每一個時間片的處理速度很是快,所以,用戶看來這些任務好像是同時執行的.
併發:
指兩個或多個任務在同一時間間隔內發生,可是,在任意一個時間點上,CPU只會處理一個任務.
多線程的優點:
1> 充分發揮多核處理器優點,將不一樣線程任務分配給不一樣的處理器,真正進入"並行運算"狀態;
2> 將耗時的任務分配到其餘線程執行,由主線程負責統一更新界面會使應用程序更加流暢,用戶體驗更好;
3> 當硬件處理器的數量增長,程序會運行更快,而程序無需作任何調整.
弊端:
新建線程會消耗內存空間和CPU時間,線程太多會下降系統的運行性能.
1.NSThread:
1> 使用NSThread對象創建一個線程很是方便;
2> 可是!要使用NSThread管理多個線程很是困難,不推薦使用;
3> 技巧!使用[NSThread currentThread]跟蹤任務所在線程,適用於這三種技術.
2.NSOperation/NSOperationQueue:
1> 是使用GCD實現的一套Objective-C的API;
2> 是面向對象的多線程技術;
3> 提供了一些在GCD中不容易實現的特性,如:限制最大併發數量,操做之間的依賴關係.
3.GCD---Grand Central Dispatch:
1> 是基於C語言的底層API;
2> 用Block定義任務,使用起來很是靈活便捷;
3> 提供了更多的控制能力以及操做隊列中所不能使用的底層函數.
iOS的開發者須要瞭解三種多線程技術的基本使用,由於在實際開發中會根據實際狀況選擇不一樣的多線程技術.
GCD的基本思想就是將操做S放在隊列S中去執行.
1> 操做使用Blocks定義;
2> 隊列負責調度任務執行所在的線程以及具體的執行時間;
3> 隊列的特色是先進先出(FIFO)的,新添加至隊列的操做都會排在隊尾.
提示:
GCD的函數都是以dispatch(分派/調度)開頭的.
dispatch_queue_t
串行隊列: 隊列中的任務只會順序執行;
並行隊列: 隊列中的任務一般會併發執行.
dispatch_async 異步操做,會併發執行,沒法肯定任務的執行順序;
dispatch_sync 同步操做,會依次順序執行,可以決定任務的執行順序.
隊列不是線程,也不表示對應的CPU.隊列就是負責調度的.多線程技術的目的,就是爲了在一個CPU上實現快速切換!
在串行隊列中:
同步操做不會新建線程,操做順序執行(沒用!);
異步操做會新建線程,操做順序執行(很是有用!) (應用場景:既不影響主線程,又須要順序執行的操做).
在並行隊列中:
同步操做不會新建線程,操做順序執行;
異步操做會新建多個線程,操做無序執行(有用,容易出錯),隊列前若是有其餘任務,會等待前面的任務完成以後再執行.應用場景:既不影響主線程,又不須要順序執行的操做.
全局隊列:
全局隊列是系統的,直接拿過來(GET)用就能夠,與並行對立相似,但調試時,沒法確認操做所在隊列.
主隊列:
每個應用程序都對應惟一一個主隊列,直接GET便可,在多線程開發中,使用主隊列更新UI;
注意:
主隊列中的操做都應該在主線程上順序執行,不存在異步的概念.
若是把主線程中的操做看做是一個大的Block,那麼除非主線程被用戶殺掉,不然永遠不會結束.因此主隊列中添加的同步操做永遠不會被執行,會死鎖.
1
2
3
4
5
6
7
8
|
// 全局隊列,都在主線程上執行,不會死鎖
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 並行隊列,都在主線程上執行,不會死鎖
dispatch_queue_t q = dispatch_queue_create(
"m.baidu.com"
, DISPATCH_QUEUE_CONCURRENT);
// 串行隊列,會死鎖,可是會執行嵌套同步操做以前的代碼
dispatch_queue_t q = dispatch_queue_create(
"m.baidu.com"
, DISPATCH_QUEUE_SERIAL);
// 直接死鎖
dispatch_queue_t q = dispatch_get_main_queue();
|
阻塞並行隊列的執行,要求某一操做執行後再進行後續操做,如用戶登陸.
確保塊代碼以外的局部變量確實被修改.
[NSThread sleepForTimeInterval:2.0f] 一般在多線程調試中用於模擬耗時操做,在發佈的應用程序中,不要使用此方法!
不管什麼隊列和什麼任務,線程的建立和回收都不須要程序員參與.線程的建立回收工做是由隊列負責的.
1> 經過GCD,開發者不用再直接跟線程打交道,只須要向隊列中添加代碼塊便可.
2> GCD在後端管理着一個線程池,GCD不只決定着代碼塊將在哪一個線程被執行,它還根據可用的系統資源對這些線程進行管理,從而讓開發者從線程管理的工做中解放出來;經過集中的管理線程,緩解大量線程被建立的問題.
3> 使用GCD,開發者能夠將工做考慮爲一個隊列,而不是一堆線程,這種並行的抽象模型更容易掌握和使用.
蘋果官方給出的GCD隊列示意圖:
從中能夠看出: GCD公開有5個不一樣的隊列:運行在主線程中的主隊列,3個不一樣優先級的後臺隊列以及一個優先級更低的後臺隊列(用於I/O).
自定義隊列:串行和並行隊列.自定義隊列很是強大,建議在開發中使用.
在自定義隊列中被調度的全部Block最終都將被放入到系統的全局隊列中和線程池中.
提示:
不建議使用不一樣優先級的隊列,由於若是設計不當,可能會出現優先級反轉,即低優先級的操做阻塞高優先級的操做.
1> NSOperationQueue(操做隊列)是由GCD提供的隊列模型的Cocoa抽象,是一套Objective-C的API;
2> GCD提供了更加底層的控制,而NSOperationQueue(操做隊列)則在GCD之上實現了一些方便的功能,這些功能對開發者而言一般是最好最安全的選擇.
NSOperationQueue有兩種不一樣類型的隊列:主隊列和自定義隊列.
主隊列運行在主線程上,自定義隊列在後臺執行.
隊列處理的任務是NSOperation的子類:NSInvocationOperation 和 NSBlockOperation.
NSOperation的基本使用步驟:
定義操做隊列 --> 定義操做 -->將操做添加到隊列.
提示:
一旦將操做添加到隊列,操做就會當即被調度執行.
1> 定義隊列:
1
|
self.myQueue = [[NSOpertaionQueue alloc] init];
|
2> 操做調用的方法:
1
2
3
4
|
-(
void
)operationAction:(id)obj
{
NSLog(@
"%@----obj : %@ "
,[NSThread currentThread], obj);
};
|
3> 定義操做並添加到隊列:
1
2
3
|
NSInvocationOperation *op = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(operationAction:) object:@(i)];
[self.myQueue addOperation:op]
|
提示:須要準備一個被調度的方法,而且可以接收一個參數.
定義操做並添加到隊列:
1
2
3
4
|
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[self operationAction:@
"Block Operation"
];
}];
[self.myQueue addOperation:op];
|
NSBlockOperation比NSInvocationOperation更加靈活;
設置操做的依賴關係:
利用 " addDependency "能夠指定操做之間彼此的依賴關係(執行前後順序),可是注意不要出現循環依賴.
設置同時併發的線程數量:
1
|
[self.myQueue setMaxConcurrentOperationCount:2];
|
從本質上看,操做隊列的性能會比GCD略低,不過,大多數狀況下這點負面影響能夠忽略不計.操做隊列是併發編程的首選工具.
在這裏,推薦一個很是好用的第三方編程框架AFN,底層用GCD開發,開發的接口是NSOperation的.
多線程中得循環引用問題:
若是self對象持有操做對象的引用,同時操做對象當中又直接訪問了self時,纔會形成循環引用.
單純在操做對象中使用self不會形成循環引用.
注意: 此時不要使用[weakSelf].
多線程中的資源共享問題:
併發編程中許多問題的根源就是在多線程中訪問共享資源.資源能夠是一個屬性,一個對象,網絡設備或者一個文件等.
在多線程中任何一個共享的資源均可能是一個潛在的衝突點,必須精心設計以防止這種衝突的發生.
爲了保證性能,atomic僅針對屬性的setter方法作了保護.
爭搶共享資源時,若是涉及到屬性的getter方法,可使用互斥鎖(@synchronized)能夠保證屬性在多個線程之間的讀寫都是安全的.
不管是atomic仍是@synchronized ,使用的代價都是高昂的.
建議:
多線程是併發執行多個任務提升效率的,若是可能,應該在線程中避免爭搶共享資源.
正是出於性能的考慮,UIKit中的絕大多數類都不是線程安全的,所以,蘋果公司要求:更新UI相關的操做,應該在主線程中執行.
1> 開啓後臺執行任務的方法:
1
|
- (
void
)performSelectorInBackground:(SEL)@Selector withObject:(id)arg
|
2> 在後臺線程中通知主線程執行任務的方法:
1
|
- (
void
)performSelectorOnMainThread:(SEL)@Selector withObject:(id)arg waitUntilDone:(
BOOL
)wait
|
3> 獲取線程信息
1
|
[NSThread currentThread]
|
4> 線程休眠
1
|
[NSThread sleepForTimeInterval:2.0f];
|
特色:
1> 使用簡單,輕量級;
2> 不能控制線程的數量以及執行順序.
NSObject的多線程方法注意事項:
1> NSObject的多線程方法使用的是NSThread的多線程技術.
2> NSThread的多線程技術不會自動使用@autoreleasepool.
在使用NSObject或NSThread的多線程技術時,若是涉及到對象分配,須要手動添加@autoreleasepool.