Grand Central Dispatch (GCD) 是Apple開發的一個多核編程的較新的解決方法。它主要用於優化應用程序以支持多核處理器以及其他對稱多處理系統。它是一個在線程池模式的基礎上執行的並行任務,在ios4以後使用
使用GCD的好處
- GCD可用於多核的並行運算(這點可以更好的發揮多核的優勢)
- GCD會自動的利用調度更多的CPU內核
- GCD是自動管理線程的生命週期(創建,任務調度 ,線程銷燬)
- 只需編寫業務邏輯不需要編寫線程管理代碼
2.任務與隊列
- 任務:任務就是放在線程中要執行的內容,在GCD Block中需要執行的內容,
- 執行任務分爲同步執行和異步執行區別在於是否具備開啓線程的能力
- 同步執行(sync):只能在當前線程中執行任務,不具備開啓線程的能力
- 異步執行(async):可以在新的線程中執行任務,具備開啓新線程的能力
隊列: 這裏的隊列指任務隊列,即用來存放任務的隊列。隊列是一種特殊的線性表,採用FIFO(先進先出)的原則,即新任務總是被插入到隊列的末尾,而讀取任務的時候總是從隊列的頭部開始讀取。每讀取一個任務,則從隊列中釋放一個任務。在GCD中有兩種隊列:串行隊列和並行隊列
- 並行隊列 (Concurrent): 可以讓多個任務並行(同時)執行
- 並行功能只在異步(dispatch_async)函數下有效
- 串行隊列(Serial) 任務順序執行的隊列
3.GCD的使用
GCD使用分爲兩步
1. 創建一個隊列(串行隊列和並行隊列)
2. 將任務添加到隊列中去
1.隊列的創建
- 使用dispatch_queue_create 創建對象,傳入兩個參數,第一個參數是隊列的唯一標誌符,可爲空,第二個參數用來標誌隊列的類型(串行隊列Serial 並行隊列Concurrent)
// 串行隊列的創建方法
dispatch_queue_t queue= dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);
// 並行隊列的創建方法
dispatch_queue_t queue= dispatch_queue_create("queueConcurrent", DISPATCH_QUEUE_CONCURRENT);
- 並行隊列可以使用dispatch_get_global_queue來創建全局的並行隊列,默認提供的是全局的並行隊列 ,需要傳入兩個參數,一般是默認參數0,0 或者 0,DISPATCH_QUEUE_PRIORITY_DEFAULT
2.任務的創建
// 同步執行任務創建方法
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 這裏放任務代碼
});
// 異步執行任務創建方法
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 這裏放任務代碼
});
上面是簡單的對隊列和任務的創建,在實際使用中我們大多使用的組合形式的使用
- 並行同步執行
- 並行異步執行
- 串行同步執行
- 串行異步執行
下面來看一下具體的執行
1.並行同步執行
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConCurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"=======開始執行");
//同步執行
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
});
NSLog(@"=======執行結束");
看一下輸出結果
從圖片中可以看出,任務是順序執行的,
2.並行異步執行
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConCurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"=======開始執行");
//異步執行
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
NSLog(@"=======執行結束");
執行結果看出任務是先被先加入線程隊列中後纔開始執行的,並且順序交替執行任務
3.串行同步執行
- 不會開啓新的線程,在當前線程執行任務,任務是串行執行,執行完一個任務,在執行下一個任務
//串行同步執行
dispatch_queue_t SerialQueue = dispatch_queue_create("SerialCurrent", DISPATCH_QUEUE_SERIAL);
NSLog(@"=======開始執行");
dispatch_sync(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_sync(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
NSLog(@"=======執行結束");
任務是順序執行,介於開始於結束之間,說明任務被添加到隊列後立即執行,感覺和並行同步執行一樣
4.串行異步執行
- 會開啓線程新線程,但因爲是串行隊列,所以會順訊執行,一個任務結束纔會執行下一個任務
dispatch_queue_t SerialQueue = dispatch_queue_create("SerialCurrent", DISPATCH_QUEUE_SERIAL);
NSLog(@"=======開始執行");
dispatch_async(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
NSLog(@"=======執行結束");
從打印結果來看,任務是先都添加到隊列中才執行的,由於是串行隊列所以程序是順序執行的,
GCD中一個重要的函數使用(dispatch_group_notify)
在實際開發中,多任務的執行,我們最終想獲得任務都已執行完畢的通知就需要使用到(dispatch_group_notify)
具體的使用法
//這裏創建的是一個全局的並行
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務一");
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務二");
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務三");
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任務執行完成");
});
執行結果可以看出任務dispatch_get_global_queue是一個並行隊列(全局的並行隊列) notify在其他任務都執行完成後纔會執行
4.GCD中另一個很重要的函數dispatch_barrier_async
從字面上理解就是一個柵欄,就好像是一道隔離牆一樣,將上下分離隔開,開發中應用場景爲,上面添加到隊列的線程任務執行完成在執行其他添加到隊列中的線程任務
dispatch_queue_t barriQueue = dispatch_queue_create("barri", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"=======開始執行");
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_barrier_async(barriQueue, ^{
NSLog(@"barri =====%@",[NSThread currentThread]);
});
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"4----%@",[NSThread currentThread]);
}
});
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"5----%@",[NSThread currentThread]);
}
});
NSLog(@"=======執行結束");
}
2017-11-02 11:38:37.035 TestApp[6549:670187] =======開始執行
2017-11-02 11:38:37.036 TestApp[6549:670187] =======執行結束
2017-11-02 11:38:37.036 TestApp[6549:670603] 2----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 1----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 2----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 1----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 2----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 1----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] barri =====<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 4----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 5----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 4----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 5----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.037 TestApp[6549:670582] 4----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.037 TestApp[6549:670603] 5----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
執行結果看,雖然我們創建的是並行異步執行,但是barri以上的執行完成以後纔會執行到barri下面的代碼
再添加一個很重要的概念,GCD的信號量(dispatch_semaphore_t)—也是線程安全的,當信號不爲0是程序等待
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t sema = dispatch_semaphore_create(1); //創建信號量 傳入的(1)爲初始號量
NSMutableArray *array = [NSMutableArray arrayWithCapacity:1];
for (int i = 0; i < 10; i++) {
@autoreleasepool {
dispatch_async(queue, ^{ //異步線程,不阻塞其他運行
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);//等待信號(信號量減一) ,在這之前不爲0纔會繼續執行,爲0就不會往下執行
NSLog(@"信號減");
[array addObject:[NSNumber numberWithInt:i]];
dispatch_semaphore_signal(sema); //信號量加一
NSLog(@"信號加");
});
}
}