IOS開發之多線程技術
來自: http://www.cnblogs.com/Jepson1218/p/5180196.html
本篇爭取一篇講清講透,篇幅將會過長,但依然將通過四大方面清晰的對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實現多線程</pre>
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創建線程的三種方式</pre>
2.1)線程屬性
1. name - 線程名稱
2. threadPriority - 線程優先級
取值范圍從 0~1.0
1.0表示優先級最高
0.0表示優先級最低
默認優先級是0.5
3. stackSize - 棧區大小
4. isMainThread - 是否主線程
2.2)資源共享(搶奪)
1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源,比如多個線程訪問同一個對象、同一個變量、同一個文件當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題。如購買火車票問題:
解決方案:
#pragma markpragma 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; } }
}
}
賣票系統線程同步解決線程不安全問題</pre>
- 互斥鎖 :如果發現有其他線程正在執行鎖定的代碼,線程會 進入休眠狀態 ,等待其他線程執行完畢,打開鎖之后,線程會被 喚醒
- 自旋鎖 :如果發現有其他線程正在執行鎖定的代碼,線程會以 死循環 的方式,一直等待鎖定代碼執行完成
</ul>
線程安全
多個線程進行讀寫操作時,仍然能夠得到正確結果,被稱為線程安全,要實現線程安全,必須要用到鎖、為了得到更佳的用戶體驗,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;
<p>@synchronized(self):互斥鎖死亡:+ (void)exit </p>
正常死亡
線程執行完畢
非正常死亡
當滿足某個條件后,在線程內部自己中止執行(自殺),[NSThread exit];
當滿足某個條件后,在主線程給其它線程打個死亡標記(下圣旨),讓子線程自行了斷.(被逼著死亡)
注意:在終止線程之前,應該注意釋放之前分配的對象!