GCD常用和不常用API說明和Demo演示,讓你輕松不費腦力的理解GCD的應用場景和操作姿勢

前言

各路大神對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]);


}

```

參考資料:

*iOS多線程:『GCD』詳盡總結

* Objective-C 高級編程 iOS 與 OS X 多線程和內存管理

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • NSThread 第一種:通過NSThread的對象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 854評論 0 3
  • 0. Description iOS SDK >= 6.0且開啟ARC,GCD對象就不應該使用dispatch_r...
    barbere閱讀 376評論 0 0
  • #import "ViewController.h" @interface ViewController () @...
    艾克12138閱讀 225評論 0 0
  • 阿涼是一個活潑好動的小姑娘,長得算是清秀??墒菗Q牙晚,初一的時候還在換牙,笑起來缺了兩顆牙的笑容毫無美感可言,偏偏...
    琉璃的流火閱讀 427評論 5 14
  • 累而不想睡, 人很難得看清自己吧?
    我有個帥哥夢閱讀 182評論 0 0