1、多線程概念程序員
進程面試
正在進行中的程序被稱爲進程,負責程序運行的內存分配。每個進程都有本身獨立的虛擬內存空間。 算法
線程編程
線程是進程中一個獨立的執行路徑(控制單元)後端
一個進程中至少包含一條線程,即主線程安全
能夠將耗時的執行路徑(如:網絡請求)放在其餘線程中執行網絡
建立線程的目的就是爲了開啓一條新的執行路徑,運行指定的代碼,與主線程中的代碼實現同時運行。多線程
棧區:主線程棧區的1M,很是很是寶貴。一個進程,至少有一個線程(主線程),不能殺掉一個線程!可是能夠暫停、休眠。併發
2、多任務系統調度示意圖異步
說明:每一個應用程序由操做系統分配的短暫的時間片(Timeslice)輪流使用CPU,因爲CPU對每一個時間片的處理速度很是快,所以,用戶看來好像這些任務在同時執行的。
併發:指兩個或多個任務在同一時間間隔內發生,可是,在任意一個時刻點上,CPU只會處理一個任務。
3、優點、弊端以及誤區
優點:
(1) 充分發揮多核處理器優點,將不一樣線程任務分配給不一樣的處理器,真正進入「並行運算」狀態
(2) 將耗時的任務分配到其餘線程執行,由主線程負責統一更新界面會使應用程序更加流暢,用戶體驗更好
(3) 當硬件處理器的數量增長,程序會運行更快,而程序無需作任何調整
弊端:
新建線程會消耗內存空間和CPU時間,線程太多會下降系統的運行性能
誤區:
多線程技術是爲了併發執行多項任務,不會提升單個算法自己的執行效率
4、iOS的三種多線程技術
NSThread
(1) 使用NSThread對象創建一個線程很是方便
(2) 可是!要使用NSThread管理多個線程很是困難,不推薦使用
(3) 技巧!使用[NSThread currentThread]得到任務所在線程,適用於這三種技術
(4) 使線程休眠0.3秒:[NSThread sleepForTimeInterval:0.3f];
NSOperation/NSOperationQueue
(1)是使用GCD實現的一套Objective-C的API
(2)是面向對象的線程技術
(3)提供了一些在GCD中不容易實現的特性,如:限制最大併發數量、操做之間的依賴關係
GCD —— Grand Central Dispatch
(1)是基於C語言的底層API
(2)用Block定義任務,使用起來很是靈活便捷
(3)提供了更多的控制能力以及操做隊列中所不能使用的底層函數
提示:iOS的開發者,須要瞭解三種多線程技術的基本使用,由於在實際開發中會根據實際狀況選擇不一樣的多線程技術。
4、GCD基本思想
GCD的基本思想是就將操做s放在隊列s中去執行。
(1) 操做使用Blocks定義
(2) 隊列負責調度任務執行所在的線程以及具體的執行時間
(3) 隊列的特色是先進先出(FIFO)的,新添加至對列的操做都會排在隊尾
提示:GCD的函數都是以dispatch(分派、調度)開頭的
隊列:
dispatch_queue_t
串行隊列,隊列中的任務只會順序執行
並行隊列,隊列中的任務一般會併發執行
操做:
(1)dispatch_async 異步操做,會併發執行,沒法肯定任務的執行順序
(2)dispatch_sync 同步操做,會依次順序執行,可以決定任務的執行順序
5、串行隊列
例如:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@", [NSThread currentThread]);
[self gcdDemo1];
}
- (void)gcdDemo1
{
// 將操做放在隊列中
// 在C語言函數中,定義類型,絕大多數的結尾是_t或者ref
// 使用串行隊列的異步任務很是很是很是有用!新建子線程是有開銷的,不能無休止新建線程
// 便可以保證效率(新建一個子線程),用可以實現併發
// 應用案例:
// 1> 從網絡上上下載圖片
// 2> 濾鏡(高光,紅眼...)
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL);
// [NSThread currentThread]得到當前線程
for (int i = 0; i < 10; ++i) {
// 異步任務順序執行,可是若是用在串行隊列中,仍然會依次順序執行
dispatch_async(q, ^{
NSLog(@"%@%d ", [NSThread currentThread],i);
});
}
輸出打印結果:
每次運行結果都是同樣的:
若是將上述代碼中異步任務順序執行部分代碼改成以下同步任務順序執行:
for (int i = 0; i < 10; ++i) {
// 同步任務順序執行
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
則輸出打印結果爲:
上述同步任務順序執行在實際開發中幾乎用不到,但面試可能會問到。
6、並行隊列
例如:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@", [NSThread currentThread]);
[self gcdDemo2];
}
- (void)gcdDemo2
{
// 特色:沒有隊形,執行順序程序員不能控制!
// 應用場景:併發執行任務,沒有前後順序關係
// 並行隊列容易出錯!並行隊列不能控制新建線程的數量!
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcd2", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; ++i) {
// 異步任務順序執行
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
}
輸出打印結果:
每次運行結果num的值和最後面的數字都不同:
若是將上述代碼中異步任務順序執行部分代碼改成以下同步任務順序執行:
for (int i = 0; i < 10; ++i) {
// 同步任務順序執行
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
則運行輸出打印結果爲:
7、全局隊列
例如(與並行隊列效果同樣):
#pragma mark - 全局隊列(蘋果爲了方便多線程的設計,提供一個全局隊列,供全部的APP共同使用)
- (void)gcdDemo3
{
// 全局隊列與並行隊列的區別
// 1> 不須要建立,直接GET就能用
// 2> 兩個隊列的執行效果相同
// 3> 全局隊列沒有名稱,調試時,沒法確認準確隊列
// 記住:在開發中永遠用DISPATCH_QUEUE_PRIORITY_DEFAULT
// 多線程的優先級反轉!低優先級的線程阻塞了高優先級的線程!
dispatch_queue_t q =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 10; ++i) {
// 同步任務順序執行
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
for (int i = 0; i < 10; ++i) {
// 異步任務,併發執行,可是若是在穿行隊列中,仍然會依次順序執行
dispatch_async(q, ^{
// [NSThread currentThread] 能夠在開發中,跟蹤當前線程
// num = 1,表示主線程
// num = 2,表示第2個子線程。。。
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
}
運行打印結果:
8、主隊列(主線程隊列)
例如:
- (void)gcdDemo4
{
// 每個應用程序都只有一個主線程
// 爲何須要在主線程上工做呢?
// 在iOS開發中,全部UI的更新工做,都必須在主線程上執行!
dispatch_queue_t q = dispatch_get_main_queue();
// 主線程是有工做的,並且除非將程序殺掉,不然主線程的工做永遠不會結束!
//開啓同步任務會致使主線程阻塞
//dispatch_sync(q, ^{
// NSLog(@"come here baby!");
// });
// 異步任務,在主線程上運行,同時是保持隊形的
for (int i = 0; i < 10; ++i) {
dispatch_async(q, ^{
NSLog(@"%@ - %d", [NSThread currentThread], i);
});
}
}
運行輸出打印結果:
9、不一樣隊列中嵌套dispatch_sync的結果
// 全局隊列,都在主線程上執行,不會死鎖
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 並行隊列,都在主線程上執行,不會死鎖
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_CONCURRENT);
// 串行隊列,會死鎖,可是會執行嵌套同步操做以前的代碼
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL);
// 直接死鎖
dispatch_queue_t q = dispatch_get_main_queue();
dispatch_sync(q, ^{
NSLog(@"同步任務 %@", [NSThread currentThread]);
dispatch_sync(q, ^{
NSLog(@"同步任務 %@", [NSThread currentThread]);
});
});
10、dispatch_sync的應用場景
阻塞並行隊列的執行,要求某一操做執行後再進行後續操做,如用戶登陸,確保塊代碼以外的局部變量確實被修改。
11、編程選擇
串行隊列,同步任務:不須要新建線程
串行隊列,異步任務:須要一個子線程,線程的建立和回收不須要程序員參與!是最安全的一個選擇。
並行隊列,同步任務:不須要建立線程
並行隊列,異步任務:有多少個任務,就開N個線程執行,
不管什麼隊列和什麼任務,線程的建立和回收不須要程序員參與。
線程的建立回收工做是由隊列負責的
「併發」編程,爲了讓程序員從負責的線程控制中解脫出來!只須要面對隊列和任務
12、GCD總結
GCD
(1) 經過GCD,開發者不用再直接跟線程打交道,只須要向隊列中添加代碼塊便可
(2) GCD在後端管理着一個線程池,GCD不只決定着代碼塊將在哪一個線程被執行,它還根據可用的系統資源對這些線程進行管理。從而讓開發者從線程管理的工做中解放出來,經過集中的管理線程,緩解大量線程被建立的問題
(3) 使用GCD,開發者能夠將工做考慮爲一個隊列,而不是一堆線程,這種並行的抽象模型更容易掌握和使用
(4) 串行中,同步中嵌套同步會致使阻塞
GCD的隊列
(1)GCD公開有5個不一樣的隊列:運行在主線程中的主隊列,3 個不一樣優先級的後臺隊列,以及一個優先級更低的後臺隊列(用於 I/O)
(2)自定義隊列:串行和並行隊列。自定義隊列很是強大,建議在開發中使用。在自定義隊列中被調度的全部Block最終都將被放入到系統的全局隊列中和線程池中
(3)在執行任務的時候,首先執行隊列中第一個加入的任務,再執行第二個、第三個….依次執行
(4)提示:不建議使用不一樣優先級的隊列,由於若是設計不當,可能會出現優先級反轉,即低優先級的操做阻塞高優先級的操做
綜上:
GCD的任務
1> disptach_sync 沒有建立線程的慾望,就在當前線程執行
最主要的目的,阻塞並行隊列任務的執行,只有當前的同步任務執行完畢後,後續的任務纔可以執行
應用場景:用戶登陸!
2> dispatch_async 有建立線程的慾望,可是建立多少條線程,取決與隊列的類型
GCD的隊列
1> 串行隊列 相似於跑步,只有一條跑道,最多可以有兩條
若是存在異步任務,就會在新線程中執行異步任務,而同步任務依舊在當前線程中執行
2> 並行隊列 相似與賽跑,具體跑道的數量,由系統決定
GCD隊列示意圖: