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、并行隊列
[self gcddemo2];
- (void)gcddemo2
// 特點:沒有隊形,執行順序程式員不能控制!
// 應用場景:并發執行任務,沒有先後順序關系
// 并行隊列容易出錯!并行隊列不能控制建立線程的數量!
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcd2", dispatch_queue_concurrent);
for (int i = 0; i < 10; ++i) {
// 異步任務順序執行
每次運作結果num的值和最後面的數字都不一樣:
則運作輸出列印結果為:
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);
// 異步任務,并發執行,但是如果在穿行隊列中,仍然會依次順序執行
// [nsthread currentthread] 可以在開發中,跟蹤目前線程
// num = 1,表示主線程
// num = 2,表示第2個子線程。。。
運作列印結果:
8、主隊列(主線程隊列)
- (void)gcddemo4
// 每一個應用程式都隻有一個主線程
// 為什麼需要在主線程上工作呢?
// 在ios開發中,所有ui的更新工作,都必須在主線程上執行!
dispatch_queue_t q = dispatch_get_main_queue();
// 主線程是有工作的,而且除非将程式殺掉,否則主線程的工作永遠不會結束!
//開啟同步任務會導緻主線程阻塞
//dispatch_sync(q, ^{
// nslog(@"come here baby!");
// });
// 異步任務,在主線程上運作,同時是保持隊形的
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隊列示意圖: