目錄
- GCD的基本概念
- Dispatch Queue
- Dispatch Group
一、GCD的基本概念
多線程的基本概念就不多介紹了,簡單介紹幾個名詞:同步,異步,串行,并發(fā)。
同步(dispatch_sync): 同步只需要記住一點(diǎn),就是不會開新的線程,所以如果用dispatch_sync處理串行和并發(fā),因?yàn)闆]有開新的線程處理結(jié)果是一樣的。
// 串行隊(duì)列
dispatch_queue_t serial_queue = dispatch_queue_create("com.zyh.www", DISPATCH_QUEUE_SERIAL);
for (int index = 0; index<10; index++)
{
dispatch_sync(serial_queue, ^{
NSLog(@"index = %d", index);
NSLog(@"%@",[NSThread currentThread]);
});
}
// 并發(fā)隊(duì)列
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.zyh.www", DISPATCH_QUEUE_CONCURRENT);
for (int index = 0; index<10; index++)
{
dispatch_sync(serial_queue, ^{
NSLog(@"index = %d", index);
NSLog(@"%@",[NSThread currentThread]);
});
}
GCDDemo[2285:242931] index = 0
GCDDemo[2285:242931] <NSThread: 0x600000075440>{number = 1, name = main}
GCDDemo[2285:242931] index = 1
GCDDemo[2285:242931] <NSThread: 0x600000075440>{number = 1, name = main}
GCDDemo[2285:242931] index = 2
GCDDemo[2285:242931] <NSThread: 0x600000075440>{number = 1, name = main}
GCDDemo[2285:242931] index = 3
GCDDemo[2285:242931] <NSThread: 0x600000075440>{number = 1, name = main}
GCDDemo[2285:242931] index = 4
GCDDemo[2285:242931] <NSThread: 0x600000075440>{number = 1, name = main}
因?yàn)闆]有開新的線程,不管是串行還是并發(fā)都會在主線程中執(zhí)行。
異步(dispatch_async): 如果不異步獲取主線程隊(duì)列的話,異步是會開新的線程,如果異步執(zhí)行串行隊(duì)列,就會開一條線程來執(zhí)行串行隊(duì)列,如果異步執(zhí)行并發(fā)隊(duì)列,系統(tǒng)會自動根據(jù)情況開多條線程來執(zhí)行并發(fā)隊(duì)列。大家可以把上面代碼改成異步,試一下結(jié)果看看和自己想象一樣不一樣吧。
串行(serial): 串行就像10個人排隊(duì)在一個窗口買票,只有上一個買完票,下一個人才能去買。一個人買完票就相當(dāng)于執(zhí)行一個任務(wù),串行隊(duì)列中的任務(wù),必須上一個任務(wù)完成,才能執(zhí)行下一個任務(wù)。
并發(fā)(concurrent): 并發(fā)就像10個人在不同的窗口買票,在同一個窗口還是要按照先進(jìn)先出的原則的。
二、Dispatch Queue
先看一下蘋果官方對于GCD的說明
開發(fā)者要做的只是定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)腄ispatch Queue中。
Dispatch Queue就是執(zhí)行處理的等待隊(duì)列,是按照先進(jìn)先出(FIFO)執(zhí)行處理。
dispatch_async(queue, ^{
// 需要執(zhí)行的任務(wù)
});
隊(duì)列分兩種串行和并發(fā),但是如何獲得這兩種隊(duì)列呢?有兩種方法:
- 通過dispatch_queue_create創(chuàng)建
// 串行隊(duì)列
dispatch_queue_t serial_queue = dispatch_queue_create("com.zyh.www", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.zyh.www", DISPATCH_QUEUE_CONCURRENT);
2.獲取系統(tǒng)標(biāo)準(zhǔn)提供的Dispatch Queue
系統(tǒng)提供有Main Dispatch Queue 和 Global Dispatch Queue
Main Dispatch Queue 是在主線程執(zhí)行的Dispatch Queue,因?yàn)橹骶€程只有一個,所以Main Dispatch Queue 自然是 Serial Dispatch Queue,放在Main Dispatch Queue隊(duì)列的任務(wù)是添加到主線程 Runloop里面執(zhí)行的,由于在主線程執(zhí)行,只能放一些更新UI不耗時的任務(wù)。
// 獲取main隊(duì)列
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
Global Dispatch Queue 就是Concurrent Dispatch Queue隊(duì)列了,有四個執(zhí)行優(yōu)先級
我們一般使用默認(rèn)的執(zhí)行優(yōu)先級
// global dispatch queue 默認(rèn)優(yōu)先級
dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
三、Dispatch Group
1. dispatch_group_notify
通過上面的學(xué)習(xí),我們已經(jīng)可以寫一些多線程的代碼了,先看下面這段異步并發(fā)的代碼
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.zyh.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrent_queue, ^{
// 異步并發(fā)處理任務(wù)
});
在實(shí)際編碼的過程中,這樣寫是會遇見問題的,因?yàn)槲覀儧]有辦法控制放在里面處理的任務(wù),比如讓10個人去多個窗口去買票,10個人買完票一塊回去,這種情況下,你是沒有辦法知道什么時候10個人都買票結(jié)束的,這個時候就用到Dispatch Group了,我們可以把這10個任務(wù)放到一個組里面,就可以控制放到線程里面的任務(wù)了,代碼如下
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (int index = 0; index<10; index++)
{
dispatch_group_async(group, queue, ^{
NSLog(@"買票 %d",index);
});
}
dispatch_group_notify(group, queue, ^{
NSLog(@"10個人買票結(jié)束");
});
GCDDemo[2160:224531] 買票 0
GCDDemo[2160:224533] 買票 2
GCDDemo[2160:224550] 買票 1
GCDDemo[2160:224530] 買票 3
GCDDemo[2160:224562] 買票 4
GCDDemo[2160:224531] 買票 5
GCDDemo[2160:224533] 買票 6
GCDDemo[2160:224550] 買票 8
GCDDemo[2160:224563] 買票 7
GCDDemo[2160:224530] 買票 9
GCDDemo[2160:224530] 10個人買票結(jié)束
2. dispatch_barrier_async
在我們異步并發(fā)處理任務(wù)的時候,也會遇見一個問題就是數(shù)據(jù)競爭的問題,如果有個可變數(shù)組,在這些并行的任務(wù)里面有寫入數(shù)據(jù)的有讀取數(shù)據(jù)的,這些任務(wù)都是并行的,這個時候肯定會出現(xiàn)問題,我們可以通過dispatch_barrier_async函數(shù)來解決這個問題,barrier 障礙物的意思,具體使用就是執(zhí)行完dispatch_barrier_async之前的任務(wù)之后,才會執(zhí)行dispatch_barrier_async之后的任務(wù),具體代碼如下
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (int index = 0; index<5; index++)
{
dispatch_group_async(group, queue, ^{
NSLog(@"寫入數(shù)據(jù) %d",index);
});
}
dispatch_barrier_async(queue, ^{
NSLog(@"讀取數(shù)據(jù)");
});
for (int index = 5; index<10; index++)
{
dispatch_group_async(group, queue, ^{
NSLog(@"寫入數(shù)據(jù) %d",index);
});
}
GCDDemo[2237:238409] 寫入數(shù)據(jù) 0
GCDDemo[2237:238410] 寫入數(shù)據(jù) 2
GCDDemo[2237:238417] 寫入數(shù)據(jù) 1
GCDDemo[2237:238517] 寫入數(shù)據(jù) 3
GCDDemo[2237:238518] 寫入數(shù)據(jù) 4
GCDDemo[2237:238409] 讀取數(shù)據(jù)
GCDDemo[2237:238410] 寫入數(shù)據(jù) 5
GCDDemo[2237:238417] 寫入數(shù)據(jù) 7
GCDDemo[2237:238519] 寫入數(shù)據(jù) 6
GCDDemo[2237:238517] 寫入數(shù)據(jù) 8
GCDDemo[2237:238520] 寫入數(shù)據(jù) 9