標籤:
iOS中的多線程
首先來瞭解什麼是多線程,進程和線程的區別.
進程:
進行中中的程式被稱為進程,負責程式啟動並執行記憶體配置;
每一個進程都有自己獨立的虛擬記憶體空間.
線程:(主線程最大佔1M的棧區空間,每條子線程最大佔512K的棧區空間)
線程是進程中一個獨立的執行路徑(控制單元);
一個進程中至少包含一條線程,即主線程;
可以將耗時的執行路徑(如網路請求)放在其他線程中執行;
線程不能被殺掉,但是可以暫停/休眠一條線程.
建立線程的目的:
開啟一條新的執行路徑,運行指定的代碼,與主線程中的代碼實現同時運行.
多任務調度系統:
每個應用程式由作業系統分配的短暫的時間片(Timeslice)輪流使用CPU,由於CPU對每個時間片的處理速度非常快,因此,使用者看來這些任務好像是同時執行的.
並發:
指兩個或多個任務在同一時間間隔內發生,但是,在任意一個時間點上,CPU只會處理一個任務.
多線程的優勢:
1> 充分發揮多核處理器優勢,將不同線程任務分配給不同的處理器,真正進入"並行運算"狀態;
2> 將耗時的任務分配到其他線程執行,由主線程負責統一更新介面會使應用程式更加流暢,使用者體驗更好;
3> 當硬體處理器的數量增加,程式會運行更快,而程式無需做任何調整.
弊端:
建立線程會消耗記憶體空間和CPU時間,線程太多會降低系統的運行效能.
iOS的三種多線程技術特點:
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基本思想
GCD的基本思想就是將操作S放在隊列S中去執行.
1> 操作使用Blocks定義;
2> 隊列負責調度任務執行所在的線程以及具體的執行時間;
3> 隊列的特點是先進先出(FIFO)的,新添加至隊列的操作都會排在隊尾.
提示:
GCD的函數都是以dispatch(指派/調度)開頭的.
隊列:
dispatch_queue_t
串列隊列: 隊列中的任務只會順序執行;
並行隊列: 隊列中的任務通常會並發執行.
操作:
dispatch_async 非同步作業,會並發執行,無法確定任務的執行順序;
dispatch_sync 同步操作,會依次順序執行,能夠決定任務的執行順序.
隊列不是線程,也不表示對應的CPU.隊列就是負責調度的.多線程技術的目的,就是為了在一個CPU上實現快速切換!
在串列隊列中:
同步操作不會建立線程,操作順序執行(沒用!);
非同步作業會建立線程,操作順序執行(非常有用!) (應用情境:既不影響主線程,又需要順序執行的操作).
在並行隊列中:
同步操作不會建立線程,操作順序執行;
非同步作業會建立多個線程,操作無序執行(有用,容易出錯),隊列前如果有其他任務,會等待前面的任務完成之後再執行.應用情境:既不影響主線程,又不需要順序執行的操作.
全域隊列:
全域隊列是系統的,直接拿過來(GET)用就可以,與並行對立類似,但調試時,無法確認操作所在隊列.
主隊列:
每一個應用程式都對應唯一一個主隊列,直接GET即可,在多線程開發中,使用主隊列更新UI;
注意:
主隊列中的操作都應該在主線程上順序執行,不存在非同步概念.
如果把主線程中的操作看作是一個大的Block,那麼除非主線程被使用者殺掉,否則永遠不會結束.所以主隊列中添加的同步操作永遠不會被執行,會死結.
不同隊列中嵌套同步操作dispatch_sync的結果:?
| 12345678 |
// 全域隊列,都在主線程上執行,不會死結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(); |
同步操作dispatch_sync的應用情境:
阻塞並行隊列的執行,要求某一操作執行後再進行後續操作,如使用者登入.
確保塊代碼之外的局部變數確實被修改.
[NSThread sleepForTimeInterval:2.0f] 通常在多線程調試中用於類比耗時操作,在發布的應用程式中,不要使用此方法!
無論什麼隊列和什麼任務,線程的建立和回收都不需要程式員參與.線程的建立回收工作是由隊列負責的.
GCD優點:
1> 通過GCD,開發人員不用再直接跟線程打交道,只需要向隊列中添加代碼塊即可.
2> GCD在後端管理著一個線程池,GCD不僅決定著代碼塊將在哪個線程被執行,它還根據可用的系統資源對這些線程進行管理,從而讓開發人員從線程管理的工作中解放出來;通過集中的管理線程,緩解大量線程被建立的問題.
3> 使用GCD,開發人員可以將工作考慮為一個隊列,而不是一堆線程,這種並行的抽象模型更容易掌握和使用.
GCD隊列:
蘋果官方給出的GCD隊列:
從中可以看出: GCD公開有5個不同的隊列:運行在主線程中的主隊列,3個不同優先順序的後台隊列以及一個優先順序更低的後台隊列(用於I/O).
自訂隊列:串列和並行隊列.自訂隊列非常強大,建議在開發中使用.
在自訂隊列中被調度的所有Block最終都將被放入到系統的全域隊列中和線程池中.
提示:
不建議使用不同優先順序的隊列,因為如果設計不當,可能會出現優先順序反轉,即低優先順序的操作阻塞高優先順序的操作.
NSOperation&NSOperationQueue簡介:
1> NSOperationQueue(操作隊列)是由GCD提供的隊列模型的Cocoa抽象,是一套Objective-C的API;
2> GCD提供了更加底層的控制,而NSOperationQueue(操作隊列)則在GCD之上實現了一些方便的功能,這些功能對開發人員而言通常是最好最安全的選擇.
隊列及操作:
NSOperationQueue有兩種不同類型的隊列:主隊列和自訂隊列.
主隊列運行在主線程上,自訂隊列在後台執行.
隊列處理的任務是NSOperation的子類:NSInvocationOperation 和 NSBlockOperation.
NSOperation的基本使用步驟:
定義操作隊列 --> 定義操作 -->將操作添加到隊列.
提示:
一旦將操作添加到隊列,操作就會立即被調度執行.
NSInvocationOperation(調度操作)
1> 定義隊列:
?
| 1 |
self.myQueue = [[NSOpertaionQueue alloc] init]; |
2> 操作調用的方法:
?
| 1234 |
-(void)operationAction:(id)obj{ NSLog(@"%@----obj : %@ ",[NSThread currentThread], obj);}; |
3> 定義操作並添加到隊列:
?
| 123 |
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@(i)];[self.myQueue addOperation:op] |
提示:需要準備一個被調度的方法,並且能夠接收一個參數.
NSBlockOperation(塊操作)
定義操作並添加到隊列:
?
| 1234 |
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ [self operationAction:@"Block Operation"];}];[self.myQueue addOperation:op]; |
NSBlockOperation比NSInvocationOperation更加靈活;
設定作業的依賴關係:
利用 " addDependency "可以指定操作之間彼此的依賴關係(執行先後順序),但是注意不要出現循環相依性.
設定同時並發的線程數量:
?
| 1 |
[self.myQueue setMaxConcurrentOperationCount:2]; |
NSOperation小結:
從本質上看,操作隊列的效能會比GCD略低,不過,大多數情況下這點負面影響可以忽略不計.操作隊列是並發編程的首選工具.
在這裡,推薦一個非常好用的第三方編程架構AFN,底層用GCD開發,開發的介面是NSOperation的.
多線程中得循環參考問題:
如果self對象持有操作對象的引用,同時操作對象當中又直接存取了self時,才會造成循環參考.
單純在操作對象中使用self不會造成循環參考.
注意: 此時不要使用[weakSelf].
多線程中的資源共用問題:
並發編程中許多問題的根源就是在多線程中訪問共用資源.資源可以是一個屬性,一個對象,網路裝置或者一個檔案等.
在多線程中任何一個共用的資源都可能是一個潛在的衝突點,必須精心設計以防止這種衝突的發生.
為了保證效能,atomic僅針對屬性的setter方法做了保護.
爭搶共用資源時,如果涉及到屬性的getter方法,可以使用互斥鎖(@synchronized)可以保證屬性在多個線程之間的讀寫都是安全的.
無論是atomic還是@synchronized ,使用的代價都是高昂的.
建議:
多線程是並發執行多個任務提高效率的,如果可能,應該線上程中避免爭搶共用資源.
正是出於效能的考慮,UIKit中的絕大多數類都不是安全執行緒的,因此,蘋果公司要求:更新UI相關的操作,應該在主線程中執行.
NSObject的多線程方法
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
iOS中的多線程