本篇爭取一篇講清講透,依然將通過四大方面清晰的對iOS開發中多線程的用法進行詳盡的講解: 一、什麼是多線程 1)多線程執行原理 2)線程與進程 3)多線程的優缺點 二、我們為什麼要用多線程編程技術 三、如何使用多線程技術 1)pthread技術 2)NSThread技術 2.1)線程屬性 2.2)資
本篇爭取一篇講清講透,依然將通過四大方面清晰的對iOS開發中多線程的用法進行詳盡的講解:
一、什麼是多線程
1)多線程執行原理
2)線程與進程
3)多線程的優缺點
二、我們為什麼要用多線程編程技術
三、如何使用多線程技術
1)pthread技術
2)NSThread技術
2.1)線程屬性
2.2)資源共用(搶奪)
3)GCD技術
4) NSOperation技術
四、線程的生命周期(線程狀態)
一、什麼是多線程
多線程(英語:multithreading),是指從軟體或者硬體上實現多個線程併發執行的技術。具有多線程能力的電腦因有硬體支持而能夠在同一時間執行多於一個線程,進而提升整體處理性能。具有這種能力的系統包括對稱多處理機、多核心處理器以及晶元級多處理(Chip-level multithreading)或同時多線程(Simultaneous multithreading)處理器。在一個程式中,這些獨立運行的程式片段叫作“線程”(Thread),利用它編程的概念就叫作“多線程處理(Multithreading)”。具有多線程能力的電腦因有硬體支持而能夠在同一時間執行多於一個線程(臺灣譯作“執行緒”),進而提升整體處理性能。
1) 多線程執行原理
a. (單核CPU)同一時間,cpu只能處理1個線程,只有1個線程在執行
b. 多線程同時執行:是CPU快速的在多個線程之間的切換
c. cpu調度線程的時間足夠快,就造成了多線程的"同時"執行
d. 如果線程數非常多,cpu會在n個線程之間切換,消耗大量的cpu資源
i. 每個線程被調度的次數會降低,線程的執行效率降低
2)線程與進程
每個正在系統上運行的程式都是一個進程。每個進程包含一到多個線程。進程也可能是整個程式或者是部分程式的動態執行。線程是一組執行的集合,或者是程式的特殊段,它可以在程式里獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程式里執行多任務。通常由操作系統負責多個線程的調度和執行。
線程是程式中一個單一的順序控制流程,在單個程式中同時運行多個線程完成不同的工作,稱為多線程。
線程和進程的區別在於,子進程和父進程有不同的代碼和數據空間,而多個線程則共用數據空間,每個線程有自己的執行堆棧和程式計數器為其執行上下文,多線程主要是為了節約CPU時間,發揮利用,根據具體情況而定。線程的運行中需要使用電腦的記憶體資源和CPU。
3)多線程的優缺點
優點:
1、使用線程可以把占據時間長的程式中的任務放到後臺去處理。
2、用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度。
3、程式的運行速度可能加快。
4、在一些等待的任務實現上如用戶輸入、文件讀寫和網路收發數據等,線程就比較有用了。在這種情況下可以釋放一些珍貴的資源如記憶體占用等等。
5、線程上的任務執行完成後,線程會自動銷毀。
缺點:
1、線程越多,cpu在調用線程上的開銷就越大,如果有大量的線程,會影響性能,因為操作系統需要在它們之間切換。
2、開啟線程需要占用一定的記憶體空間(預設情況下,每一個線程都占512KB),如果開啟大量的線程,會占用大量的記憶體空間,降低程式的性能,更多的線程需要更多的記憶體空間。
3、程式設計更加複雜,比如線程間的通信、多線程的數據共用,可能會給程式帶來更多的BUG,因此要小心使用。
4、線程的中止需要考慮其對程式運行的影響。
5、通常塊模型數據是在多個線程間共用的,需要一個合適的鎖系統替換掉數據共用。
註意:iOS 8.0後主線程的預設堆棧大小也是 512K,官方文檔標註錯誤。
二、我們為什麼要用多線程編程技術
在大多數研究領域內是要求線程調度程式要能夠快速選擇其中一個已就緒線程去運行,而不是一個一個運行而降低效率。所以要讓調度程式去分辨線程的優先順序是很重要的。在移動開發過程中,一切均已用戶體驗作為首要任務,這時多線程的重要性不言而喻。
一個程式運行後,預設會開啟1個線程,稱為“主線程”或“UI線程”,主線程一般用來刷新UI界面,處理UI事件(比如:點擊、滾動、拖拽等事件)
主線程使用註意
別將耗時的操作放到主線程中
耗時操作會卡住主線程,嚴重影響UI的流暢度,給用戶一種卡的壞體驗
三、如何使用多線程技術
ios中多線程實現的多種技術方案:
POSIX 表示可移植操作系統介面(Portable Operating System Interface )-----pthread
1)pthread技術:
pthread 是 POSIX 多線程開發框架,由於是跨平臺的 C 語言框架,在蘋果的頭文件中並沒有詳細的註釋要查閱 pthread 有關資料,可以訪問 http://baike.baidu.com
// 創建線程,並且線上程中執行 demo 函數 - (void)pthreadDemo { /** 參數: 1> 指向線程標識符的指針,C 語言中類型的結尾通常 _t/Ref,而且不需要使用 * 2> 用來設置線程屬性 3> 新建立的線程執行代碼的函數 4> 運行函數的參數 返回值: - 若線程創建成功,則返回0 - 若線程創建失敗,則返回出錯編號 在混合開發時,如果在 C 和 OC 之間傳遞數據,需要使用 __bridge 進行橋接,橋接的目的就是為了告訴編譯器如何管理記憶體 */ pthread_t threadId = NULL; NSString *str = @"Hello Pthread"; int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(str)); result ? NSLog(@"為其他任何值時代表開闢子線程失敗") : NSLog(@"當result為0時表示開闢子線程成功"); } // 後臺線程調用函數 void *demo(void *params) { NSString *str = (__bridge NSString *)(params); NSLog(@"%@ - %@", [NSThread currentThread], str); return NULL; }C語言中pthread.h里pthread實現多線程
2)NSThread技術:
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"主線程%@", [NSThread currentThread]); /** 多個線程之間的執行順序是隨機的 */ // 方式1:通過NSThread的對象方法 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"方式1"]; [thread start]; // 方式2:沒有thread字眼,隱式創建並啟動線程,所有 NSObject 都可以使用此方法,在其他線程執行方法 [self performSelectorInBackground:@selector(demo:) withObject:@"方式2"]; // 方式3:detachNewThreadSelector 類方法不需要啟動,會自動創建線程並執行 @selector 方法 [NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"方式3"]; } - (void)demo:(NSString *)str { NSLog(@"%@, %@", str, [NSThread currentThread]); }通過NSThread創建線程的三種方式
2.1)線程屬性
1. name - 線程名稱
2. threadPriority - 線程優先順序
取值範圍從 0~1.0
1.0表示優先順序最高
0.0表示優先順序最低
預設優先順序是0.5
3. stackSize - 棧區大小
4. isMainThread - 是否主線程
2.2)資源共用(搶奪)
1塊資源可能會被多個線程共用,也就是多個線程可能會訪問同一塊資源,比如多個線程訪問同一個對象、同一個變數、同一個文件當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題。如購買火車票問題:
解決方案:
#pragma mark #pragma mark - 模擬賣票系統 - (void)sellTicket { _count = 50; NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(ticket) object:nil]; thread1.name = @"t1"; [thread1 start]; NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(ticket) object:nil]; thread2.name = @"t2"; [thread2 start]; } - (void)ticket { while (YES) { // 被加鎖的對象 @synchronized(self) { if (_count > 0) { _count = self.count - 1; NSLog(@"剩餘票數%ld ——%@", _count, [NSThread currentThread]); } else { NSLog(@"票賣沒了倒霉蛋"); break; } } } }賣票系統線程同步解決線程不安全問題
互斥鎖
:如果發現有其他線程正在執行鎖定的代碼,線程會進入休眠狀態
,等待其他線程執行完畢,打開鎖之後,線程會被喚醒
自旋鎖
:如果發現有其他線程正在執行鎖定的代碼,線程會以死迴圈
的方式,一直等待鎖定代碼執行完成
線程安全
多個線程進行讀寫操作時,仍然能夠得到正確結果,被稱為線程安全,要實現線程安全,必須要用到鎖、
為了得到更佳的用戶體驗,UIKit 不是線程安全的,所以更新 UI 的操作都必須主線程上執行!因此,主線程又被稱為UI 線程。
3)GCD技術
為保證篇幅不過與雜糅,請見“IOS開發之多線程技術——GCD篇”
4) NSOperation技術
為保證篇幅不過與雜糅,請見“IOS開發之多線程技術——NSOperation篇”
四、線程的生命周期(線程狀態)
新建
實例化線程對象
就緒
- (void)start;
向線程對象發送 start 消息,線程對象被加入 可調度線程池 等待 CPU 調度
detachNewThreadSelector 方法和 performSelectorInBackground 方法會直接實例化一個線程對象並加入 可調度線程池
運行
CPU 負責調度可調度線程池中線程的執行
線程執行完成之前(死亡之前),狀態可能會在就緒和運行之間來回切換
就緒和運行之間的狀態變化由 CPU 負責,程式員不能幹預
阻塞
當滿足某個預定條件時,可以使用休眠或鎖阻塞線程執行
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
@synchronized(self):互斥鎖
死亡:+ (void)exit
正常死亡
線程執行完畢
非正常死亡
當滿足某個條件後,線上程內部自己中止執行(自殺),[NSThread exit];
當滿足某個條件後,在主線程給其它線程打個死亡標記(下聖旨),讓子線程自行了斷.(被逼著死亡)
註意:在終止線程之前,應該註意釋放之前分配的對象!