前言
各路大神對GCD的原理解析和使用方法網上到處都是,可以輕松搜索到。那為什么筆者還要自己動手寫一篇所謂的"葵花寶典"呢?其實本篇文章主要是普及了一些基礎知識概念。例如隊列、線程、異步同步的區別,搞懂這些才是弄明白GCD的條件。筆者搜集了GCD的絕大部分api,包括不常用的、冷門的。這里沒有高大上的實現原理,沒有難懂的底層實現。以最淺顯的,最簡單的文字說明配上demo和代碼實例最后結合運行log,讓你輕松不費腦力的理解GCD的應用場景和操作姿勢。
[博客地址],歡迎各路大神批評指正。
基礎概念
?關于GCD:
? ? (1)是基于c語言的底層api
? ? (2)用block定義任務,使用起來非常靈活便捷
? ? (3)GCD會自動利用更多的CPU內核(比如雙核、四核)
? ? (4)GCD會自動管理線程的生命周期(創建線程、調度任務、銷毀線程)
? ? (5)程序員只需要告訴GCD想要執行什么任務,不需要編寫任何線程管理代碼
? 關于進程:
? ? 正在進行中的程序被稱為進程,負責程序運行的內存分配;每一個進程都有自己獨立的虛擬內存空間;
?關于線程:
? ? 線程是進程中一個獨立的執行路徑(控制單元);一個進程中至少包含一條線程,即主線程。
?關于隊列:
隊列用來存放任務,一種符合 FIFO(先進先出)原則的數據結構,線程的創建和回收不需要程序員操作,由隊列負責。
? ? 串行隊列:隊列中的任務只會順序執行,一個任務執行完畢后,再執行下一個任務
? ? 并發隊列:隊列中的多個任務并發(同時)執行,而且無法確定任務的執行順序 ?
? ? 全局隊列:是系統開發的,直接拿過來用就可以;與并行隊列類似,但調試時,無法確認操作所在隊列
? ? 主隊列:? 每一個應用程序對應唯一一個主隊列,是gcd中自帶的一種特殊的串行隊列,直接獲取即可。放在主隊列中的任務,都會在主線程中執行。在多線程開發中,使用主隊列更新UI。
關于操作:
? ? dispatch_async 異步操作,在新的線程中執行任務,具備開啟新線程的能力(不是百分百開啟新線程,會取決于任務所在隊列類型),會并發執行,無法確定任務的執行順序;
? ? dispatch_sync? 同步操作,在當前線程中執行任務,不具備開啟新線程的能力,會依次順序執行;
* 圖例:
使用姿勢
分為兩步:
第一步:創建一個隊列;
第二步:將任務放到隊列中;
三個關鍵點:
第一點:任務內容;
第二點:隊列類別;
第三點:操作(追加)姿勢;
隊列和任務
?1. 獲取隊列:
GCD中大體可以分為三種隊列:
* 串行隊列:
`dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);`
* 并發隊列:
* 一般并發隊列
`dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);`
* 全局并發隊列可以作為普通并發隊列來使用
`dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);`
* 主隊列:
`dispatch_queue_t queue = dispatch_get_main_queue();`
?2. 操作(追加)方式:
* 同步執行任務創建方法
```
dispatch_sync(queue, ^{
? ? NSLog(@"我是任務");
});
```
* 異步執行任務創建方法
```
dispatch_async(queue, ^{
? ? NSLog(@"我是任務");
});
```
3. 隊列 + 任務:
##### 3.1? 隊列 + 任務的六種組合
看到這里你不難發現,GCD 提供了同步執行任務的創建方法`dispatch_sync`和異步執行任務創建方法`dispatch_async`,配合上述的三種隊列形式(串行隊列、并發隊列、主隊列),那么就會存在六種不同的多線程使用方法,如下:
*? 同步執行 & 并發隊列 : 不新建線程,在當前線程中順序執行
*? 異步執行 & 并發隊列 : 新建多個新線程,線程會復用,無序執行
*? 同步執行 & 串行隊列 : 在當前線程中順序執行
*? 異步執行 & 串行隊列 : 新建一條新的線程,在該線程中順序執行
*? 異步執行 & 主隊列? : 不新建線程,在主線程中順序執行
*? 同步執行 & 主隊列(在主線程中會crash): 主線程中會產生死鎖
##### 3.2? 各種組合的使用方法
###### 同步執行 & 并發隊列:
```
-(void)syncAndConcurrentqueue{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? //下面提供兩種并發隊列的獲取方式,其運行結果無差別,所以歸為了一類,你可以自由選擇
? ? dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
? ? // 全局并發隊列
? ? //? ? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
? ? // 第一個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第二個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第三個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
* 輸出結果:
* 總結:只會在當前線程中依次執行任務,不會開啟新線程,執行完一個任務,再執行下一個任務,按照1>2>3順序執行,遵循FIFO原則。
###### 異步執行 & 并發隊列:
```
-(void)asyncAndConcurrentqueue{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? //下面提供兩種并發隊列的獲取方式,其運行結果無差別,所以歸為了一類,你可以自由選擇
? ? //dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
? ? // 全局并發隊列
? ? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
? ? // 第一個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第二個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第三個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
* 輸出結果:
* 總結:從log中可以發現,系統另外開啟了3個線程,并且任務是同時執行的,并不是按照1>2>3順序執行。所以異步+并發隊列具備開啟新線程的能力,且并發隊列可開啟多個線程,同時執行多個任務。
###### 同步執行 & 串行隊列:
```
-(void)syncAndSerialqueue{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
? ? // 第一個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第二個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第三個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
* 輸出結果:
* 總結:只會在當前線程中依次執行任務,不會開啟新線程,執行完一個任務,再執行下一個任務,按照1>2>3順序執行,遵循FIFO原則。
###### 異步執行 & 串行隊列:
```
-(void)asyncAndSerialqueue{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
? ? // 第一個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第二個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第三個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
* 輸出結果:
* 總結:開啟了一條新線程,異步執行具備開啟新線程的能力,因為是串行隊列所以只開啟一個線程,在該線程中執行完一個任務,再執行下一個任務,按照1>2>3順序執行,遵循FIFO原則。
###### 異步執行 & 主隊列:
```
-(void)asyncAndMainqueue{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? //獲取主隊列
? ? dispatch_queue_t queue = dispatch_get_main_queue();
? ? // 第一個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第二個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第三個任務
? ? dispatch_async(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
* 輸出結果:
* 總結:所有任務都是在當前線程(主線程)中執行的,并沒有開啟新的線程(雖然異步執行具備開啟線程的能力,但因為是主隊列,所以所有任務都在主線程中),在主線程中執行完一個任務,再執行下一個任務,按照1>2>3順序執行,遵循FIFO原則。
###### 同步執行 & 主隊列(在主線程中會crash):
```
-(void)syncAndMainqueue{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? //獲取主隊列
? ? dispatch_queue_t queue = dispatch_get_main_queue();
? ? // 第一個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第二個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? // 第三個任務
? ? dispatch_sync(queue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);? ?
}
//下面的例子類似:在同一個同步串行隊列中,再使用該隊列同步執行任務也是會發生死鎖。
-(void)syncAndMainqueue1{
? ? dispatch_queue_t queue1 = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
? ? dispatch_sync(queue1, ^{
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----11111-----當前線程%@",[NSThread currentThread]);//到這里就死鎖了
? ? ? ? dispatch_sync(queue1, ^{
? ? ? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? ? ? NSLog(@"----22222---當前線程%@",[NSThread currentThread]);
? ? ? ? });
? ? ? ? NSLog(@"----333333-----當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----44444-----當前線程%@",[NSThread currentThread]);
}
```
* 輸出結果:
* 總結:直接crash。這是因為發生了死鎖,在gcd中,禁止在主隊列(串行隊列)中再以同步操作執行主隊列任務。同理,在同一個同步串行隊列中,再使用該隊列同步執行任務也是會發生死鎖。
###### 同步執行 & 主隊列(在其它線程中):
```
-(void)othersyncAndMainqueue{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
? ? // 第一個任務
? ? dispatch_async(queue, ^{
? ? ? ? NSLog(@"----執行任務---%@",[NSThread currentThread]);
? ? ? ? //獲取主隊列
? ? ? ? dispatch_queue_t queue = dispatch_get_main_queue();
? ? ? ? // 第一個任務
? ? ? ? dispatch_sync(queue, ^{
? ? ? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? });
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
* 輸出結果:
* 總結:所有任務都是在主線程(非當前線程)中執行的,沒有開啟新的線程(所有放在主隊列中的任務,都會放到主線程中執行)。在主線程中執行完一個任務,再執行下一個任務,按照1>2>3順序執行,遵循FIFO原則。
## GCD常用API及其使用方法
#### 1. Dispatch Queue:
```
//各種隊列的獲取方法
-(void)getQueue{
? ? //主隊列的獲取方法:主隊列是串行隊列,主隊列中的任務都將在主線程中執行
? ? dispatch_queue_t mainqueue = dispatch_get_main_queue();
? ? //串行隊列的創建方法:第一個參數表示隊列的唯一標識,第二個參數用來識別是串行隊列還是并發隊列(若為NULL時,默認是DISPATCH_QUEUE_SERIAL)
? ? dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
? ? //并發隊列的創建方法:第一個參數表示隊列的唯一標識,第二個參數用來識別是串行隊列還是并發隊列(若為NULL時,默認是DISPATCH_QUEUE_SERIAL)
? ? dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
? ? //全局并發隊列的獲取方法:第一個參數表示隊列優先級,我們選擇默認的好了,第二個參數flags作為保留字段備用,一般都直接填0
? ? dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}
```
#### 2. `dispatch_queue_creat`:
```
//自定義隊列的創建方法
-(void)queue_create{
? ? //串行隊列的創建方法:第一個參數表示隊列的唯一標識,第二個參數用來識別是串行隊列還是并發隊列(若為NULL時,默認是DISPATCH_QUEUE_SERIAL)
? ? dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
? ? //并發隊列的創建方法:第一個參數表示隊列的唯一標識,第二個參數用來識別是串行隊列還是并發隊列(若為NULL時,默認是DISPATCH_QUEUE_SERIAL)
? ? dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
}
```
#### 3. `dispatch_set_target_queue`:
* `dispatch_set_target_queue`可以更改`Dispatch Queue`的執行優先級
`dispatch_queue_create`函數生成的`DisPatch Queue`不管是`Serial DisPatch Queue`還是`Concurrent Dispatch Queue`,執行的優先級都與默認優先級的`Global Dispatch queue`相同,如果需要變更生成的`Dispatch Queue`的執行優先級則需要使用`dispatch_set_target_queue`函數。
```
//使用dispatch_set_target_queue更改Dispatch Queue的執行優先級
-(void)testTargetQueue1{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? //串行隊列的創建方法:第一個參數表示隊列的唯一標識,第二個參數用來識別是串行隊列還是并發隊列(若為NULL時,默認是DISPATCH_QUEUE_SERIAL)
? ? dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", NULL);
? ? //指定一個任務
? ? dispatch_async(seriaQueue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? //全局并發隊列的獲取方法:第一個參數表示隊列優先級,我們選擇默認的好了,第二個參數flags作為保留字段備用,一般都直接填0
? ? dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
? ? //指定一個任務
? ? dispatch_async(globalQueue, ^{
? ? ? ? //這里線程暫停2秒,模擬一般的任務的耗時操作
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? //第一個參數為要設置優先級的queue,第二個參數是參照物,即將第一個queue的優先級和第二個queue的優先級設置一樣。
? ? //第一個參數如果是系統提供的【主隊列】或【全局隊列】,則不知道會出現什么情況,因此最好不要設置第一參數為系統提供的隊列
? ? dispatch_set_target_queue(seriaQueue,globalQueue);
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
//dispatch_set_target_queue除了能用來設置隊列的優先級之外,還能夠創建隊列的層次體系,當我們想讓不同隊列中的任務同步的執行時,我們可以創建一個串行隊列,然后將這些隊列的target指向新創建的隊列即可。
- (void)testTargetQueue2 {
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_queue_t targetQueue = dispatch_queue_create("com.test.target_queue", DISPATCH_QUEUE_SERIAL);
? ? dispatch_queue_t queue1 = dispatch_queue_create("com.test.queue1", DISPATCH_QUEUE_SERIAL);
? ? dispatch_queue_t queue2 = dispatch_queue_create("com.test.queue2", DISPATCH_QUEUE_CONCURRENT);
? ? dispatch_set_target_queue(queue1, targetQueue);
? ? dispatch_set_target_queue(queue2, targetQueue);
? ? //指定一個異步任務
? ? dispatch_async(queue1, ^{
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? });
? ? //指定一個異步任務
? ? dispatch_async(queue2, ^{
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? });
? ? //指定一個異步任務
? ? dispatch_async(queue2, ^{
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
#### 4. dispatch_after:
```
//延時執行,需要注意的是:dispatch_after函數并不是在指定時間之后才開始執行處理,而是在指定時間之后將任務追加到主隊列中。嚴格來說,這個時間并不是絕對準確的,但想要大致延遲執行任務,dispatch_after函數是很有效的。
-(void)dispatch_after{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
? ? ? ? // 2秒后異步追加任務代碼到主隊列等待執行
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
#### 5. dispatch_once:
```
//只執行一次,通常在創建單例時使用,多線程環境下也能保證線程安全
-(void)dispatch_once_1{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? static dispatch_once_t onceToken;
? ? dispatch_once(&onceToken, ^{
? ? ? ? NSLog(@"----只執行一次的任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
#### 6. dispatch_apply:
```
//快速遍歷方法,可以替代for循環的函數。dispatch_apply按照指定的次數將指定的任務追加到指定的隊列中,并等待全部隊列執行結束。
//會創建新的線程,并發執行
-(void)dispatch_apply{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
? ? dispatch_apply(100, globalQueue, ^(size_t index) {
? ? ? ? NSLog(@"執行第%zd次的任務---%@",index, [NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
#### 7. dispatch_group:
```
//隊列組:當我們遇到需要異步下載3張圖片,都下載完之后再拼接成一個整圖的時候,就需要用到gcd隊列組。
-(void)dispatch_group{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_group_t group =? dispatch_group_create();
? ? dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
? ? ? ? // 第一個任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
? ? ? ? // 第二個任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
? ? ? ? // 第三個任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_group_notify(group, dispatch_get_main_queue(), ^{
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行最后的匯總任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? //若想執行完上面的任務再走下面這行代碼可以加上下面這句代碼
? ? // 等待上面的任務全部完成后,往下繼續執行(會阻塞當前線程)
? ? //? ? dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
//dispatch_group_enter 標志著一個任務追加到 group,執行一次,相當于 group 中未執行完畢任務數+1
//dispatch_group_leave 標志著一個任務離開了 group,執行一次,相當于 group 中未執行完畢任務數-1。
//當 group 中未執行完畢任務數為0的時候,才會使dispatch_group_wait解除阻塞,以及執行追加到dispatch_group_notify中的任務。
-(void)dispatch_group_1{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_group_t group = dispatch_group_create();
? ? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
? ? dispatch_group_enter(group);
? ? dispatch_async(queue, ^{
? ? ? ? // 第一個任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? dispatch_group_leave(group);
? ? });
? ? dispatch_group_enter(group);
? ? dispatch_async(queue, ^{
? ? ? ? // 第二個任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? dispatch_group_leave(group);
? ? });
? ? dispatch_group_enter(group);
? ? dispatch_async(queue, ^{
? ? ? ? // 第三個任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? dispatch_group_leave(group);
? ? });
? ? dispatch_group_notify(group, dispatch_get_main_queue(), ^{
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行最后的匯總任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
#### 8. dispatch_semaphore:
```
//信號量
//總結:信號量設置的是2,在當前場景下,同一時間內執行的線程就不會超過2,先執行2個線程,等執行完一個,下一個會開始執行。
-(void)dispatch_semaphore{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
? ? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
? ? //任務1
? ? dispatch_async(queue, ^{
? ? ? ? dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
? ? ? ? NSLog(@"----開始執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----結束執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? dispatch_semaphore_signal(semaphore);
? ? });
? ? //任務2
? ? dispatch_async(queue, ^{
? ? ? ? dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
? ? ? ? NSLog(@"----開始執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? [NSThread sleepForTimeInterval:1];
? ? ? ? NSLog(@"----結束執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? dispatch_semaphore_signal(semaphore);
? ? });
? ? //任務3
? ? dispatch_async(queue, ^{
? ? ? ? dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
? ? ? ? NSLog(@"----開始執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----結束執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? ? ? dispatch_semaphore_signal(semaphore);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
#### 9. Dispatch I/O:
```
//以下為蘋果中使用Dispatch I/O和Dispatch Data的例子
pipe_q = dispatch_queue_create("PipeQ",NULL);
pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM,fd,pipe_q,^(int err){
? close(fd);
});
*out_fd = fdpair[i];
dispatch_io_set_low_water(pipe_channel,SIZE_MAX);
dispatch_io_read(pipe_channel,0,SIZE_MAX,pipe_q, ^(bool done,dispatch_data_t pipe data,int err){
? if(err == 0)
? ? {
? ? ? size_t len = dispatch_data_get_size(pipe data);
? ? ? if(len > 0)
? ? ? {
? ? ? ? ? const char *bytes = NULL;
? ? ? ? ? char *encoded;
? ? ? ? ? dispatch_data_t md = dispatch_data_create_map(pipe data,(const void **)&bytes,&len);
? ? ? ? ? asl_set((aslmsg)merged_msg,ASL_KEY_AUX_DATA,encoded);
? ? ? ? ? free(encoded);
? ? ? ? ? _asl_send_message(NULL,merged_msg,-1,NULL);
? ? ? ? ? asl_msg_release(merged_msg);
? ? ? ? ? dispatch_release(md);
? ? ? }
? ? ? }
? ? ? if(done)
? ? ? {
? ? ? ? dispatch_semaphore_signal(sem);
? ? ? ? dispatch_release(pipe_channel);
? ? ? ? dispatch_release(pipe_q);
? ? ? }
});
```
#### 10. dispatch_barrier_async:
```
//隔斷方法:當前面的寫入操作全部完成之后,再執行后面的讀取任務。當然也可以用Dispatch Group和dispatch_set_target_queue,只是比較而言,dispatch_barrier_async會更加順滑
-(void)dispatch_barrier_async{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
? ? dispatch_async(queue, ^{
? ? ? ? // 第一個寫入任務
? ? ? ? [NSThread sleepForTimeInterval:3];
? ? ? ? NSLog(@"----執行第一個寫入任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_async(queue, ^{
? ? ? ? // 第二個寫入任務
? ? ? ? [NSThread sleepForTimeInterval:1];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_barrier_async(queue, ^{
? ? ? ? // 等待處理
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----等待前面的任務完成---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_async(queue, ^{
? ? ? ? // 第一個讀取任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第一個讀取任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_async(queue, ^{
? ? ? ? // 第二個讀取任務
? ? ? ? [NSThread sleepForTimeInterval:2];
? ? ? ? NSLog(@"----執行第二個讀取任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
#### 11. dispatch_suspend & dispatchp_resume:
```
//場景:當追加大量處理到Dispatch Queue時,在追加處理的過程中,有時希望不執行已追加的處理。例如演算結果被Block截獲時,一些處理會對這個演算結果造成影響。在這種情況下,只要掛起Dispatch Queue即可。當可以執行時再恢復。
//總結:dispatch_suspend,dispatch_resume提供了“掛起、恢復”隊列的功能,簡單來說,就是可以暫停、恢復隊列上的任務。但是這里的“掛起”,并不能保證可以立即停止隊列上正在運行的任務,也就是如果掛起之前已經有隊列中的任務在進行中,那么該任務依然會被執行完畢
-(void)dispatch_suspend{
? ? NSLog(@"----start-----當前線程---%@",[NSThread currentThread]);
? ? dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
? ? dispatch_async(queue, ^{
? ? ? ? // 執行第一個任務
? ? ? ? [NSThread sleepForTimeInterval:5];
? ? ? ? NSLog(@"----執行第一個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_async(queue, ^{
? ? ? ? // 執行第二個任務
? ? ? ? [NSThread sleepForTimeInterval:5];
? ? ? ? NSLog(@"----執行第二個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? dispatch_async(queue, ^{
? ? ? ? // 執行第三個任務
? ? ? ? [NSThread sleepForTimeInterval:5];
? ? ? ? NSLog(@"----執行第三個任務---當前線程%@",[NSThread currentThread]);
? ? });
? ? //此時發現意外情況,掛起隊列
? ? NSLog(@"suspend");
? ? dispatch_suspend(queue);
? ? //掛起10秒之后,恢復正常
? ? [NSThread sleepForTimeInterval:10];
? ? //恢復隊列
? ? NSLog(@"resume");
? ? dispatch_resume(queue);
? ? NSLog(@"----end-----當前線程---%@",[NSThread currentThread]);
}
```
參考資料:
* Objective-C 高級編程 iOS 與 OS X 多線程和內存管理