iOS多線程之GCD
版權屬于: LvesLi’s Blogging
原文地址: http://www.lvesli.com/?p=276
轉載時必須以鏈接形式注明原始出處及本聲明。
GCD是iOS中經常使用的多線程技術,是一個重點也是一個難點,今天來看看GCD怎么使用。
一、優勢
1.Apple為多核并行計算提出的解決方案
2.自動管理線程的生命周期
二、同步和異步,串行隊列和并行隊列
學習GCD首先要了解同步和異步與串行和并行之間的關系
1.同步和異步區別就是是否創建新的線程,同步函數一定不會開辟新的線程,異步函數具有開辟多線程的能力,到底開不開還要看使用的是什么隊列。
2.串行隊列:一個任務執行完才能執行下一個任務;
并行隊列:并行隊列中的任務可以同時執行;
三、隊列
Dispatch Queue是一個任務執行隊列,可以讓你異步或同步地執行多個Block或函數。Dispatch Queue是FIFO的,即先入隊的任務總會先執行。目前有三種類型的Dispath Queue:
1.串行隊列(Serial dispatch queue)
串行隊列中一次只能處理一個任務(一個任務執行完畢后,再執行下一個任務),調用dispatch_queue_create創建;
dispatch_queue_t myQueue=dispatch_queue_create(“www.lvesli.com”, NULL);
2.并發隊列(Concurrent dispatch queue)
并行隊列中任務可以同時被執行(自動開啟多個線程同時執行任務)。全局隊列(Global Concurrent Dispatch Queues)。目前系統預定義了四個不同運行優先級的全局隊列,我們可以通過dispatch_get_global_queue來獲取它們。
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue第一個參數是隊列的優先級,分別對應四個全局隊列:
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
3.主隊列(Main dispatch queue)
主隊列是一個特殊的串行隊列,它是系統預定義的運行在主線程的一個Dispatch Queue。可以通過dispatch_get_main_queue來獲取唯一的主隊列。主隊列中的任務一定是在主線程中執行的。
四、執行任務
同步函數,用同步的方式執行任務
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
異步函數,用異步的方式執行任務
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
下面舉個栗子:
1>異步執行,圖片下載
//1. 調用異步函數執行全局并行隊列中的人物 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //執行下載圖片的邏輯 NSLog(@"下載圖片。。。 %@",[NSThread currentThread]); //2. 下載完畢,回到主線程更新UI dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"更新UI。。。 %@",[NSThread currentThread]); }); });
執行結果如下:
2015-07-20 22:29:44.205 GCD-0707[598:1093914] 下載圖片。。。 {number = 2, name = (null)}
2015-07-20 22:29:44.256 GCD-0707[598:1092383] 更新UI。。。 {number = 1, name = main}
2>延時執行
//延時執行 NSLog(@"執行之前。。。 %@",[NSThread currentThread]); dispatch_time_t afterSecond= dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)); //兩秒后異步執行 dispatch_after(afterSecond, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"我是兩秒后執行的。。。 %@",[NSThread currentThread]); });
執行結果如下:
2015-07-20 22:45:43.856 GCD-0707[725:1364233] 執行之前。。。 {number = 1, name = main}
2015-07-20 22:45:45.947 GCD-0707[725:1364530] 我是兩秒后執行的。。。 {number = 2, name = (null)}
3>一次性執行代碼
// 一次性執行: static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"%@",@"只執行一次"); });
3>隊列組
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 執行1個耗時的異步操作 NSLog(@"執行1個耗時的異步操作。。。 %@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 執行1個耗時的異步操作 NSLog(@"執行1個耗時的異步操作。。。 %@",[NSThread currentThread]); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的異步操作都執行完畢后,回到主線程更新UI... NSLog(@"回到主線程 更新UI。。。 %@",[NSThread currentThread]); });
執行結果如下:
2015-07-20 22:57:50.221 GCD-0707[829:1569205] 執行1個耗時的異步操作。。。 {number = 3, name = (null)}
2015-07-20 22:57:50.221 GCD-0707[829:1565558] 執行1個耗時的異步操作。。。 {number = 2, name = (null)}
2015-07-20 22:57:50.222 GCD-0707[829:1565281] 回到主線程 更新UI。。。 {number = 1, name = main}
4>并行執行某段代碼n次
//執行某個代碼片段N次。 dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) { // 執行5次 NSLog(@"%@",[NSThread currentThread]); });
執行結果如下:
2015-07-20 23:18:55.104 GCD-0707[1083:1926255] {number = 1, name = main}
2015-07-20 23:18:55.104 GCD-0707[1083:1926527] {number = 2, name = (null)}
2015-07-20 23:18:55.104 GCD-0707[1083:1926523] {number = 4, name = (null)}
2015-07-20 23:18:55.104 GCD-0707[1083:1926524] {number = 3, name = (null)}
2015-07-20 23:18:55.105 GCD-0707[1083:1926255] {number = 1, name = main}
5>補充
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"111"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"222"); NSLog(@"歡迎關注微信公眾賬號:lecoding"); }); NSLog(@"333"); }
執行結果: 只執行第一句輸出,就卡死了,無法向下執行。在主線程中執行串行函數調用主隊列中的任務就會造成相互等待狀態,造成死循環。
2015-07-20 23:07:53.975 GCD-0707[985:1740328] 111