GCD

什麼是GCD

全稱是Grand Central Dispatch。GCD 全部的API都在libdispatch.dylib動態庫裏面Xcode自動導入這個庫。html

 

GCD的優點

1)GCD是蘋果公司爲多核的並行運算提出的解決方案;ios

2)GCD會自動利用更多的CPU內核;程序員

3)GCD會自動管理線程的聲明週期(建立、調度和銷燬。方便程序員的使用)segmentfault

4)GCD 能夠經過延遲耗時任務,並讓他們在後臺運行,從而提升應用的響應速度。數組

5)對於線程和鎖來講,GCD 提供了更簡便的方法,從而避免發生併發問題(e.g.使用單例模式,GCD能夠用來優化咱們的代碼。)併發

 

GCD相關概念

1.任務app

執行的操做,GCD遵循:FIFO,先進先出,後進後出,先放進去的任務,先拿出來執行。異步

2.隊列:串行和並行async

併發和串行決定了任務的執行方式;ide

併發隊列:多個任務併發(同時)執行;

串行隊列:讓任務一個接着一個的執行(必須一個任務結束了再取出下一個任務執行);

把任務添加到隊列中:GCD會自動將隊列中的任務取出,放到對應的線程中執行。

3.同步和異步

同步和異步決定了可否開啓新的線程;

同步:只在當前線程中執行任務,不會從新開啓線程(需立刻執行);

異步:能夠在新的線程中執行任務,具有從新開啓線程的能力(不會立刻執行,何時線程有空再去執行);

 

GCD 函數:

// 1.獲取隊列

/**
 *  @brief 建立主隊列(一種特殊的串行隊列)
 *
 *  @return 返回主隊列
 */
dispatch_queue_t dispatch_get_main_queue(void)

/**
 *  @brief 建立隊列
 *
 *  @param label 隊列標籤,注意爲 const char* 類型
 *  @param attr  隊列屬性(同步隊列:DISPATCH_QUEUE_SERIAL/NULL; 異步隊列:DISPATCH_QUEUE_CONCURRENT)
 *
 *  @return 返回建立的同步或異步隊列
 */
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

/**
 *  @brief 得到全局隊列
 *
 *  @param identifier 優先級
                     通常寫 0(能夠適配 iOS7/iOS8)
                     iOS7
                     DISPATCH_QUEUE_PRIORITY_HIGH      2  高優先級
                     DISPATCH_QUEUE_PRIORITY_DEFAULT   0  默認優先級
                     DISPATCH_QUEUE_PRIORITY_LOW     -2  低優先級
                     DISPATCH_QUEUE_PRIORITY_BACKGROUND 後臺優先級
                     
                     iOS8
             QQS_CLASS_USER_INITIATED 2 高優先級 QOS_CLASS_DEFAULT     0 默認優先級
             QQS_CLASS_UTILITY    -2 低優先級
             QQS_CLASS_BACKGROUND 後臺優先級(最低) * @param flags 保留參數 0 * * @return 返回全局隊列
*/ dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags); // 2.GCD中有兩個用來執行任務的函數 /** * @brief 同步函數 * * @param queue 隊列 * @param block 任務 */ void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); /** * @brief 異步函數 * * @param queue 隊列 * @param block 任務 */ void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

 

隊列和同/異部函數組合:

/**
 全局隊列跟併發隊列的區別
 1. 全局隊列沒有名稱 併發隊列有名稱
 2. 全局隊列,是供全部的應用程序共享。
 3. 在MRC開發,併發隊列,建立完了,須要釋放。 全局隊列不須要咱們管理
 */
#pragma CGD - 全局隊列
- (void)GCD_Global_queue {
    // 得到全局隊列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 添加異步任務
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"currentThread=%@ i=%d", [NSThread currentThread], i);
        });
    }
}
打印結果:
2016-04-05 08:04:38.004 05-GCD的使用[7353:1070557] currentThread=<NSThread: 0x7f92c8e49e10>{number = 3, name = (null)} i=1
2016-04-05 08:04:38.004 05-GCD的使用[7353:1070556] currentThread=<NSThread: 0x7f92cb006340>{number = 5, name = (null)} i=0
2016-04-05 08:04:38.004 05-GCD的使用[7353:1070440] currentThread=<NSThread: 0x7f92c8e3a540>{number = 4, name = (null)} i=2
2016-04-05 08:04:38.005 05-GCD的使用[7353:1070603] currentThread=<NSThread: 0x7f92c8e03e70>{number = 6, name = (null)} i=3
2016-04-05 08:04:38.005 05-GCD的使用[7353:1070448] currentThread=<NSThread: 0x7f92c8d02b90>{number = 2, name = (null)} i=4
2016-04-05 08:04:38.005 05-GCD的使用[7353:1070557] currentThread=<NSThread: 0x7f92c8e49e10>{number = 3, name = (null)} i=5
分析:全局隊列是一種特殊的併發隊列。


#pragma GCD- 同步任務的做用
- (void)GCD_Concurrent_Sync {
    // 併發隊列
    dispatch_queue_t  queue = dispatch_queue_create("BD", DISPATCH_QUEUE_CONCURRENT);
    
    // 添加任務
    // 同步任務,須要立刻執行。 不開新線程
    dispatch_sync(queue, ^{
        NSLog(@"刷新界面 %@", [NSThread currentThread]);
    });
    // 異步任務,不會立刻執行。開新線程
    dispatch_async(queue, ^{
        NSLog(@"下載圖片AA %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"下載圖片BB %@", [NSThread currentThread]);
    });
}
打印結果:
2016-04-05 08:06:21.669 05-GCD的使用[7369:1073636] 刷新界面 <NSThread: 0x7f97c0704dc0>{number = 1, name = main}
2016-04-05 08:06:21.670 05-GCD的使用[7369:1074613] 下載圖片BB <NSThread: 0x7f97c0403f00>{number = 4, name = (null)}
2016-04-05 08:06:21.670 05-GCD的使用[7369:1074608] 下載圖片AA <NSThread: 0x7f97c0615070>{number = 3, name = (null)}



#pragma GCD-主隊列
/**
  主隊列:專門負責在主線程上調度任務,不會在子線程調度任務,在主隊列不容許開新線程.
  同步執行:要立刻執行
  結果:死鎖
 */
- (void)GCD_Main_queue_sync {
    // 1. 得到主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    NSLog(@"1----");
    
    // 2. 同步執行任務
    for (int i = 0; i < 6; i++) {
        NSLog(@"調度前---");
        // 同步:把任務放到主隊列裏,但需是立刻執行
        dispatch_sync(queue, ^{
            NSLog(@"currentThread=%@ i=%d", [NSThread currentThread], i);
        });
        NSLog(@"Wait one second……");
        [NSThread sleepForTimeInterval:1.0];
    }
    NSLog(@"完成----");
}
打印結果:
2016-04-05 08:00:56.137 05-GCD的使用[7340:1062486] 1----
2016-04-05 08:00:56.138 05-GCD的使用[7340:1062486] 調度前---
分析: 產生了死鎖!!同步任務須要立刻執行,可是主線程正在執行該函數這個任務,因此須要等該函數執行完,可是該函數也在等這個任務執行完畢,因此形成了死鎖!



/**
 主隊列:專門負責在主線程上調度任務,不會在子線程調度任務,在主隊列不容許開新線程.
 異步執行: 會開新線程,在新線程執行
 結果: 不開線程, 只能在主線程上面,順序執行!
 */
- (void)GCD_Main_queue_async {
    // 1. 得到主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    NSLog(@"1----");
    
    // 2. 異步執行任務
    for (int i = 0; i < 6; i++) {
        NSLog(@"調度前---");
        // 異步:把任務放到主隊列裏,可是不須要立刻執行
        dispatch_async(queue, ^{
            NSLog(@"currentThread=%@ i=%d", [NSThread currentThread], i);
        });
        NSLog(@"Wait one second……");
        [NSThread sleepForTimeInterval:1.0];
    }
    NSLog(@"完成----");
}
打印結果:
2016-04-05 07:56:43.596 05-GCD的使用[7327:1054178] 1----
2016-04-05 07:56:43.596 05-GCD的使用[7327:1054178] 調度前---
2016-04-05 07:56:43.596 05-GCD的使用[7327:1054178] Wait one second……
2016-04-05 07:56:44.598 05-GCD的使用[7327:1054178] 調度前---
2016-04-05 07:56:44.598 05-GCD的使用[7327:1054178] Wait one second……
2016-04-05 07:56:45.599 05-GCD的使用[7327:1054178] 調度前---
2016-04-05 07:56:45.599 05-GCD的使用[7327:1054178] Wait one second……
2016-04-05 07:56:46.600 05-GCD的使用[7327:1054178] 調度前---
2016-04-05 07:56:46.601 05-GCD的使用[7327:1054178] Wait one second……
2016-04-05 07:56:47.601 05-GCD的使用[7327:1054178] 調度前---
2016-04-05 07:56:47.601 05-GCD的使用[7327:1054178] Wait one second……
2016-04-05 07:56:48.602 05-GCD的使用[7327:1054178] 調度前---
2016-04-05 07:56:48.603 05-GCD的使用[7327:1054178] Wait one second……
2016-04-05 07:56:49.604 05-GCD的使用[7327:1054178] 完成----
2016-04-05 07:56:49.605 05-GCD的使用[7327:1054178] currentThread=<NSThread: 0x7ff208f00ad0>{number = 1, name = main} i=0
2016-04-05 07:56:49.605 05-GCD的使用[7327:1054178] currentThread=<NSThread: 0x7ff208f00ad0>{number = 1, name = main} i=1
2016-04-05 07:56:49.614 05-GCD的使用[7327:1054178] currentThread=<NSThread: 0x7ff208f00ad0>{number = 1, name = main} i=2
2016-04-05 07:56:49.614 05-GCD的使用[7327:1054178] currentThread=<NSThread: 0x7ff208f00ad0>{number = 1, name = main} i=3
2016-04-05 07:56:49.615 05-GCD的使用[7327:1054178] currentThread=<NSThread: 0x7ff208f00ad0>{number = 1, name = main} i=4
2016-04-05 07:56:49.615 05-GCD的使用[7327:1054178] currentThread=<NSThread: 0x7ff208f00ad0>{number = 1, name = main} i=5
分析:異步任務放到隊列中,並不會立刻執行,而主線程爲串行隊列,裏面的任務(該函數)會立刻執行,等待任務(該函數)執行完畢(完成--),再去執行異步任務!!!!!



#pragma GCD測試
/**
  併發隊列:能夠同時執行多個任務
  同步任務:不會開闢新線程,是在當前線程執行
  結果:不開新線程,順序一個一個執行。
 */
- (void)GCD_Concurrent_sync {
    NSLog(@"----1");
    //1. 並行隊列
    dispatch_queue_t queue = dispatch_queue_create("BD", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 同步執行任務
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"currentThread=%@ i=%d", [NSThread currentThread], i);
        });
    }
    NSLog(@"----2");
}
打印結果:
2016-04-05 08:08:19.435 05-GCD的使用[7399:1078990] ----1
2016-04-05 08:08:19.436 05-GCD的使用[7399:1078990] currentThread=<NSThread: 0x7fa64b506580>{number = 1, name = main} i=0
2016-04-05 08:08:19.436 05-GCD的使用[7399:1078990] currentThread=<NSThread: 0x7fa64b506580>{number = 1, name = main} i=1
2016-04-05 08:08:19.436 05-GCD的使用[7399:1078990] currentThread=<NSThread: 0x7fa64b506580>{number = 1, name = main} i=2
2016-04-05 08:08:19.436 05-GCD的使用[7399:1078990] currentThread=<NSThread: 0x7fa64b506580>{number = 1, name = main} i=3
2016-04-05 08:08:19.436 05-GCD的使用[7399:1078990] currentThread=<NSThread: 0x7fa64b506580>{number = 1, name = main} i=4
2016-04-05 08:08:19.436 05-GCD的使用[7399:1078990] currentThread=<NSThread: 0x7fa64b506580>{number = 1, name = main} i=5
2016-04-05 08:08:19.436 05-GCD的使用[7399:1078990] ----2
分析:同步任務不會開闢新線程,是在當前線程(主線程)執行

/**
 併發隊列:能夠同時執行多個任務
 異步執行:會開新線程,在新線程執行
 結果:會開不少個線程,同時執行
 */
- (void)GCD_Concurrent_async {
    //1. 並行隊列
    dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 異步執行任務
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"currentThread=%@ i=%d", [NSThread currentThread], i);
        });
    }
}
打印結果:
2016-04-05 08:09:35.973 05-GCD的使用[7409:1082043] currentThread=<NSThread: 0x7fa190e016e0>{number = 13, name = (null)} i=1
2016-04-05 08:09:35.973 05-GCD的使用[7409:1082042] currentThread=<NSThread: 0x7fa190cbb050>{number = 12, name = (null)} i=0
2016-04-05 08:09:35.973 05-GCD的使用[7409:1082044] currentThread=<NSThread: 0x7fa190d4f080>{number = 14, name = (null)} i=2
2016-04-05 08:09:35.973 05-GCD的使用[7409:1082045] currentThread=<NSThread: 0x7fa190e2f850>{number = 15, name = (null)} i=@"%@", [NSThread currentThread]);
    });
    NSLog(@"完成!!");
}

 

線程間通訊(我的以爲就是爲了主線程和子線程之間通訊設計的):

    // 全局隊列,異步函數(開啓新的線程)
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"1 = %@", [NSThread currentThread]);
        
        // 耗時任務(下載圖片) : 放在全局隊列且異步執行
        NSURL *url = [NSURL URLWithString:@"http://pic16.nipic.com/20111009/4632997_120303895251_2.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        
        // 回到主線程,刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
            NSLog(@"2 = %@", [NSThread currentThread]);
        });
    });

 

iOS經常使用延遲提交:

//// NSThread
/*5];
        NSLog(@"After 5 seconds...");
    });
    
    // 提交第二個block,也是延時5秒打印
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:5];
        NSLog(@"After 5 seconds again...");
    });
    
    // 延時一秒
    NSLog(@"sleep 1 second...");
    [NSThread sleepForTimeInterval:1];
    
    // 掛起隊列
    NSLog(@"suspend...");
    dispatch_suspend(queue);
    
    // 延時10秒
    NSLog(@"sleep 10 second...");
    [NSThread sleepForTimeInterval:10];
    
    // 恢復隊列
    NSLog(@"resume...");
    dispatch_resume(queue);

打印結果:
2016-04-05 13:18:56.016 08-GCD的其餘用法[8053:1408615] sleep 1 second...
2016-04-05 13:18:57.018 08-GCD的其餘用法[8053:1408615] suspend...
2016-04-05 13:18:57.019 08-GCD的其餘用法[8053:1408615] sleep 10 second...
2016-04-05 13:19:01.018 08-GCD的其餘用法[8053:1408652] After 5 seconds...
2016-04-05 13:19:07.020 08-GCD的其餘用法[8053:1408615] resume...
2016-04-05 13:19:12.025 08-GCD的其餘用法[8053:1408652] After 5 seconds again...
分析 :在dispatch_suspend掛起隊列後,第一個block仍是在運行且正常輸出。
結論 : dispatch_suspend並不會當即暫停正在運行的隊列block內的任務,而是在當前block執行完成後,暫停後續的隊列block內任務的執行。

 

GCD實現定時器:

    __weak typeof(self) weakSelf = self;
    __block int timeCount = 30; // 實現 30s 倒計時
    // 實現倒計時功能
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        // 觸發定時器執行的代碼
        if (timeCount <= 0) {
            dispatch_source_cancel(timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"1 = %@", [NSThread currentThread]);
                NSLog(@"發送驗證碼");
            });
        } else if (timeCount > 0) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"2 = %@", [NSThread currentThread]);
                NSLog(@"timeCount = %@", [NSString stringWithFormat:@"%d後從新發送", timeCount]);
            });
            timeCount--;
        }
    });
    dispatch_resume(timer);

函數:

/**
 *  @brief 建立一個新的調度源來監視低級別的系統對象和自動提交處理程序塊來響應事件調度隊列
 *
 *  @param type   調度員支持事件類型
 *  @param handle 句柄,通常傳0
 *  @param mask   通常傳0
 *  @param queue  隊列
 *
 *  @return 返回建立的調度源
 */
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue);

/**
 *  @brief 爲一個定時源設置一個開始時間、事件間隔、偏差值
 *
 *  @param source   調度員
 *  @param start    設置開始時間
 *  @param interval 事件的時間間隔
 *  @param leeway   偏差值
 */
void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway);

 

建議:

1.慎用 dispatch_sync 同步函數,防止 deaklock(main_queue使用同步隊列會致使死鎖);

2.濫用dispatch_after作定時器致使crash,最好使用 dispatch_source_set_timer 實現倒計時功能;

 

參考 :

Grand Central Dispatch (GCD) Reference

GCD使用經驗與技巧淺談

iOS詳細介紹

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息