文章目錄
GCD簡(jiǎn)介
任務(wù)和隊(duì)列
GCD的使用步驟
隊(duì)列的創(chuàng)建方法
任務(wù)的創(chuàng)建方法
GCD的基本使用
并行隊(duì)列 + 同步執(zhí)行
并行隊(duì)列 + 異步執(zhí)行
串行隊(duì)列 + 同步執(zhí)行
串行隊(duì)列 + 異步執(zhí)行
主隊(duì)列 + 同步執(zhí)行
主隊(duì)列 + 異步執(zhí)行
GCD線程之間的通訊
GCD的其他方法
GCD的柵欄方法dispatch_barrier_async
GCD的延時(shí)執(zhí)行方法dispatch_after
GCD的一次性代碼(只執(zhí)行一次)dispatch_once
GCD的快速迭代方法dispatch_apply
GCD的隊(duì)列組dispatch_group
1. GCD簡(jiǎn)介
什么是GCD呢?我們先來看看百度百科的解釋簡(jiǎn)單了解下概念
引自百度百科
Grand Central Dispatch(GCD) 是Apple開發(fā)的一個(gè)多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對(duì)稱多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。
為什么要用GCD呢?
因?yàn)镚CD有很多好處啊,具體如下:
GCD可用于多核的并行運(yùn)算
GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)
GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
既然GCD有這么多的好處,那么下面我們就來系統(tǒng)的學(xué)習(xí)一下GCD的使用方法。
2. 任務(wù)和隊(duì)列
學(xué)習(xí)GCD之前,先來了解GCD中兩個(gè)核心概念:任務(wù)和隊(duì)列。
任務(wù):就是執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼。在GCD中是放在block中的。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行和異步執(zhí)行。兩者的主要區(qū)別是:是否具備開啟新線程的能力。
同步執(zhí)行(sync):只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
異步執(zhí)行(async):可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
隊(duì)列:這里的隊(duì)列指任務(wù)隊(duì)列,即用來存放任務(wù)的隊(duì)列。隊(duì)列是一種特殊的線性表,采用FIFO(先進(jìn)先出)的原則,即新任務(wù)總是被插入到隊(duì)列的末尾,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)。在GCD中有兩種隊(duì)列:串行隊(duì)列和并行隊(duì)列。
并行隊(duì)列(Concurrent Dispatch Queue):可以讓多個(gè)任務(wù)并行(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
并行功能只有在異步(dispatch_async)函數(shù)下才有效
串行隊(duì)列(Serial Dispatch Queue):讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))
3. GCD的使用步驟
GCD的使用步驟其實(shí)很簡(jiǎn)單,只有兩步。
創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列或并行隊(duì)列)
將任務(wù)添加到隊(duì)列中,然后系統(tǒng)就會(huì)根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)
下邊來看看隊(duì)列的創(chuàng)建方法和任務(wù)的創(chuàng)建方法。
1. 隊(duì)列的創(chuàng)建方法
可以使用dispatch_queue_create來創(chuàng)建對(duì)象,需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識(shí)符,用于DEBUG,可為空;第二個(gè)參數(shù)用來識(shí)別是串行隊(duì)列還是并行隊(duì)列。DISPATCH_QUEUE_SERIAL表示串行隊(duì)列,DISPATCH_QUEUE_CONCURRENT表示并行隊(duì)列。
// 串行隊(duì)列的創(chuàng)建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并行隊(duì)列的創(chuàng)建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
對(duì)于并行隊(duì)列,還可以使用dispatch_get_global_queue來創(chuàng)建全局并行隊(duì)列。GCD默認(rèn)提供了全局的并行隊(duì)列,需要傳入兩個(gè)參數(shù)。第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級(jí),一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個(gè)參數(shù)暫時(shí)沒用,用0即可。
2. 任務(wù)的創(chuàng)建方法
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);? ? // 這里放任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);? ? // 這里放任務(wù)代碼
});
雖然使用GCD只需兩步,但是既然我們有兩種隊(duì)列,兩種任務(wù)執(zhí)行方式,那么我們就有了四種不同的組合方式。這四種不同的組合方式是
并行隊(duì)列 + 同步執(zhí)行
并行隊(duì)列 + 異步執(zhí)行
串行隊(duì)列 + 同步執(zhí)行
串行隊(duì)列 + 異步執(zhí)行
實(shí)際上,我們還有一種特殊隊(duì)列是主隊(duì)列,那樣就有六種不同的組合方式了。
主隊(duì)列 + 同步執(zhí)行
主隊(duì)列 + 異步執(zhí)行
那么這幾種不同組合方式各有什么區(qū)別呢,這里為了方便,先上結(jié)果,再來講解。為圖省事,直接查看表格結(jié)果,然后可以跳過** 4. GCD的基本使用** 了。
并行隊(duì)列串行隊(duì)列主隊(duì)列
同步(sync)沒有開啟新線程,串行執(zhí)行任務(wù)沒有開啟新線程,串行執(zhí)行任務(wù)沒有開啟新線程,串行執(zhí)行任務(wù)
異步(async)有開啟新線程,并行執(zhí)行任務(wù)有開啟新線程(1條),串行執(zhí)行任務(wù)沒有開啟新線程,串行執(zhí)行任務(wù)
下邊我們來分別講講這幾種不同的組合方式的使用方法。
4. GCD的基本使用
先來講講并行隊(duì)列的兩種使用方法。
1. 并行隊(duì)列 + 同步執(zhí)行
不會(huì)開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
- (void) syncConcurrent
{
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");
}
輸出結(jié)果:
2016-09-03 19:22:27.577 GCD[11557:1897538] syncConcurrent---begin
2016-09-03 19:22:27.578 GCD[11557:1897538] 1------{number = 1, name = main}
2016-09-03 19:22:27.578 GCD[11557:1897538] 1------{number = 1, name = main}
2016-09-03 19:22:27.578 GCD[11557:1897538] 2------{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 2------{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 3------{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 3------{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] syncConcurrent---end
從并行隊(duì)列 + 同步執(zhí)行中可以看到,所有任務(wù)都是在主線程中執(zhí)行的。由于只有一個(gè)線程,所以任務(wù)只能一個(gè)一個(gè)執(zhí)行。
同時(shí)我們還可以看到,所有任務(wù)都在打印的syncConcurrent---begin和syncConcurrent---end之間,這說明任務(wù)是添加到隊(duì)列中馬上執(zhí)行的。
2. 并行隊(duì)列 + 異步執(zhí)行
可同時(shí)開啟多線程,任務(wù)交替執(zhí)行
- (void) asyncConcurrent
{
NSLog(@"asyncConcurrent---begin");
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"asyncConcurrent---end");
}
輸出結(jié)果:
2016-09-03 19:27:31.503 GCD[11595:1901548] asyncConcurrent---begin
2016-09-03 19:27:31.504 GCD[11595:1901548] asyncConcurrent---end
2016-09-03 19:27:31.504 GCD[11595:1901626] 1------{number = 2, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901625] 2------{number = 4, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901855] 3------{number = 3, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901626] 1------{number = 2, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901625] 2------{number = 4, name = (null)}
2016-09-03 19:27:31.505 GCD[11595:1901855] 3------{number = 3, name = (null)}
在并行隊(duì)列 + 異步執(zhí)行中可以看出,除了主線程,又開啟了3個(gè)線程,并且任務(wù)是交替著同時(shí)執(zhí)行的。
另一方面可以看出,所有任務(wù)是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執(zhí)行的。說明任務(wù)不是馬上執(zhí)行,而是將所有任務(wù)添加到隊(duì)列之后才開始異步執(zhí)行。
接下來再來講講串行隊(duì)列的執(zhí)行方法。
3. 串行隊(duì)列 + 同步執(zhí)行
不會(huì)開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
- (void) syncSerial
{
NSLog(@"syncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"syncSerial---end");
}
輸出結(jié)果為:
2016-09-03 19:29:00.066 GCD[11622:1903904] syncSerial---begin
2016-09-03 19:29:00.067 GCD[11622:1903904] 1------{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 1------{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 2------{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 2------{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 3------{number = 1, name = main}
2016-09-03 19:29:00.068 GCD[11622:1903904] 3------{number = 1, name = main}
2016-09-03 19:29:00.068 GCD[11622:1903904] syncSerial---end
在串行隊(duì)列 + 同步執(zhí)行可以看到,所有任務(wù)都是在主線程中執(zhí)行的,并沒有開啟新的線程。而且由于串行隊(duì)列,所以按順序一個(gè)一個(gè)執(zhí)行。
同時(shí)我們還可以看到,所有任務(wù)都在打印的syncConcurrent---begin和syncConcurrent---end之間,這說明任務(wù)是添加到隊(duì)列中馬上執(zhí)行的。
4. 串行隊(duì)列 + 異步執(zhí)行
會(huì)開啟新線程,但是因?yàn)槿蝿?wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
- (void) asyncSerial
{
NSLog(@"asyncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"asyncSerial---end");
}
輸出結(jié)果為:
2016-09-03 19:30:08.363 GCD[11648:1905817] asyncSerial---begin
2016-09-03 19:30:08.364 GCD[11648:1905817] asyncSerial---end
2016-09-03 19:30:08.364 GCD[11648:1905895] 1------{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 1------{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 2------{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 2------{number = 2, name = (null)}
2016-09-03 19:30:08.365 GCD[11648:1905895] 3------{number = 2, name = (null)}
2016-09-03 19:30:08.365 GCD[11648:1905895] 3------{number = 2, name = (null)}
在串行隊(duì)列 + 異步執(zhí)行可以看到,開啟了一條新線程,但是任務(wù)還是串行,所以任務(wù)是一個(gè)一個(gè)執(zhí)行。
另一方面可以看出,所有任務(wù)是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執(zhí)行的。說明任務(wù)不是馬上執(zhí)行,而是將所有任務(wù)添加到隊(duì)列之后才開始同步執(zhí)行。
下邊講講剛才我們提到過的特殊隊(duì)列——主隊(duì)列。
主隊(duì)列:GCD自帶的一種特殊的串行隊(duì)列
所有放在主隊(duì)列中的任務(wù),都會(huì)放到主線程中執(zhí)行
可使用dispatch_get_main_queue()獲得主隊(duì)列
我們?cè)賮砜纯粗麝?duì)列的兩種組合方式。
5. 主隊(duì)列 + 同步執(zhí)行
互等卡住不可行(在主線程中調(diào)用)
- (void)syncMain
{
NSLog(@"syncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"syncMain---end");
}
輸出結(jié)果
2016-09-03 19:32:15.356 GCD[11670:1908306] syncMain---begin
這時(shí)候,我們驚奇的發(fā)現(xiàn),在主線程中使用主隊(duì)列 + 同步執(zhí)行,任務(wù)不再執(zhí)行了,而且syncMain---end也沒有打印。這是為什么呢?
這是因?yàn)槲覀冊(cè)谥骶€程中執(zhí)行這段代碼。我們把任務(wù)放到了主隊(duì)列中,也就是放到了主線程的隊(duì)列中。而同步執(zhí)行有個(gè)特點(diǎn),就是對(duì)于任務(wù)是立馬執(zhí)行的。那么當(dāng)我們把第一個(gè)任務(wù)放進(jìn)主隊(duì)列中,它就會(huì)立馬執(zhí)行。但是主線程現(xiàn)在正在處理syncMain方法,所以任務(wù)需要等syncMain執(zhí)行完才能執(zhí)行。而syncMain執(zhí)行到第一個(gè)任務(wù)的時(shí)候,又要等第一個(gè)任務(wù)執(zhí)行完才能往下執(zhí)行第二個(gè)和第三個(gè)任務(wù)。
那么,現(xiàn)在的情況就是syncMain方法和第一個(gè)任務(wù)都在等對(duì)方執(zhí)行完畢。這樣大家互相等待,所以就卡住了,所以我們的任務(wù)執(zhí)行不了,而且syncMain---end也沒有打印。
要是如果不再主線程中調(diào)用,而在其他線程中調(diào)用會(huì)如何呢?
不會(huì)開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)(在其他線程中調(diào)用)
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self syncMain];
});
輸出結(jié)果:
2016-09-03 19:32:45.496 GCD[11686:1909617] syncMain---begin
2016-09-03 19:32:45.497 GCD[11686:1909374] 1------{number = 1, name = main}
2016-09-03 19:32:45.498 GCD[11686:1909374] 1------{number = 1, name = main}
2016-09-03 19:32:45.498 GCD[11686:1909374] 2------{number = 1, name = main}
2016-09-03 19:32:45.498 GCD[11686:1909374] 2------{number = 1, name = main}
2016-09-03 19:32:45.499 GCD[11686:1909374] 3------{number = 1, name = main}
2016-09-03 19:32:45.499 GCD[11686:1909374] 3------{number = 1, name = main}
2016-09-03 19:32:45.499 GCD[11686:1909617] syncMain---end
在其他線程中使用主隊(duì)列 + 同步執(zhí)行可看到:所有任務(wù)都是在主線程中執(zhí)行的,并沒有開啟新的線程。而且由于主隊(duì)列是串行隊(duì)列,所以按順序一個(gè)一個(gè)執(zhí)行。
同時(shí)我們還可以看到,所有任務(wù)都在打印的syncConcurrent---begin和syncConcurrent---end之間,這說明任務(wù)是添加到隊(duì)列中馬上執(zhí)行的。
6. 主隊(duì)列 + 異步執(zhí)行
只在主線程中執(zhí)行任務(wù),執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
- (void)asyncMain
{
NSLog(@"asyncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"asyncMain---end");
}
輸出結(jié)果:
2016-09-03 19:33:54.995 GCD[11706:1911313] asyncMain---begin
2016-09-03 19:33:54.996 GCD[11706:1911313] asyncMain---end
2016-09-03 19:33:54.996 GCD[11706:1911313] 1------{number = 1, name = main}
2016-09-03 19:33:54.997 GCD[11706:1911313] 1------{number = 1, name = main}
2016-09-03 19:33:54.997 GCD[11706:1911313] 2------{number = 1, name = main}
2016-09-03 19:33:54.997 GCD[11706:1911313] 2------{number = 1, name = main}
2016-09-03 19:33:54.997 GCD[11706:1911313] 3------{number = 1, name = main}
2016-09-03 19:33:54.997 GCD[11706:1911313] 3------{number = 1, name = main}
我們發(fā)現(xiàn)所有任務(wù)都在主線程中,雖然是異步執(zhí)行,具備開啟線程的能力,但因?yàn)槭侵麝?duì)列,所以所有任務(wù)都在主線程中,并且一個(gè)接一個(gè)執(zhí)行。
另一方面可以看出,所有任務(wù)是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執(zhí)行的。說明任務(wù)不是馬上執(zhí)行,而是將所有任務(wù)添加到隊(duì)列之后才開始同步執(zhí)行。
弄懂了難理解、繞來繞去的隊(duì)列+任務(wù)之后,我們來學(xué)習(xí)一個(gè)簡(jiǎn)單的東西——GCD線程之間的通訊。
5. GCD線程之間的通訊
在iOS開發(fā)過程中,我們一般在主線程里邊進(jìn)行UI刷新,例如:點(diǎn)擊、滾動(dòng)、拖拽等事件。我們通常把一些耗時(shí)的操作放在其他線程,比如說圖片下載、文件上傳等耗時(shí)操作。而當(dāng)我們有時(shí)候在其他線程完成了耗時(shí)操作時(shí),需要回到主線程,那么就用到了線程之間的通訊。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
// 回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2-------%@",[NSThread currentThread]);
});
});
輸出結(jié)果:
2016-09-03 19:34:59.165 GCD[11728:1913039] 1------{number = 2, name = (null)}
2016-09-03 19:34:59.166 GCD[11728:1913039] 1------{number = 2, name = (null)}
2016-09-03 19:34:59.166 GCD[11728:1912961] 2-------{number = 1, name = main}
可以看到在其他線程中先執(zhí)行操作,執(zhí)行完了之后回到主線程執(zhí)行主線程的相應(yīng)操作。
6. GCD的其他方法
1. GCD的柵欄方法dispatch_barrier_async
我們有時(shí)需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來,當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄。
- (void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
輸出結(jié)果:
2016-09-03 19:35:51.271 GCD[11750:1914724] ----1-----{number = 2, name = (null)}
2016-09-03 19:35:51.272 GCD[11750:1914722] ----2-----{number = 3, name = (null)}
2016-09-03 19:35:51.272 GCD[11750:1914722] ----barrier-----{number = 3, name = (null)}
2016-09-03 19:35:51.273 GCD[11750:1914722] ----3-----{number = 3, name = (null)}
2016-09-03 19:35:51.273 GCD[11750:1914724] ----4-----{number = 2, name = (null)}
可以看出在執(zhí)行完柵欄前面的操作之后,才執(zhí)行柵欄操作,最后再執(zhí)行柵欄后邊的操作。
2. GCD的延時(shí)執(zhí)行方法dispatch_after
當(dāng)我們需要延遲執(zhí)行一段代碼時(shí),就需要用到GCD的dispatch_after方法。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后異步執(zhí)行這里的代碼...
NSLog(@"run-----");
});
3. GCD的一次性代碼(只執(zhí)行一次)dispatch_once
我們?cè)趧?chuàng)建單例、或者有整個(gè)程序運(yùn)行過程中只執(zhí)行一次的代碼時(shí),我們就用到了GCD的dispatch_once方法。使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});
4. GCD的快速迭代方法dispatch_apply
通常我們會(huì)用for循環(huán)遍歷,但是GCD給我們提供了快速迭代的方法dispatch_apply,使我們可以同時(shí)遍歷。比如說遍歷0~5這6個(gè)數(shù)字,for循環(huán)的做法是每次取出一個(gè)元素,逐個(gè)遍歷。dispatch_apply可以同時(shí)遍歷多個(gè)數(shù)字。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd------%@",index, [NSThread currentThread]);
});
輸出結(jié)果:
2016-09-03 19:37:02.250 GCD[11764:1915764] 1------{number = 1, name = main}
2016-09-03 19:37:02.250 GCD[11764:1915885] 0------{number = 2, name = (null)}
2016-09-03 19:37:02.250 GCD[11764:1915886] 2------{number = 3, name = (null)}
2016-09-03 19:37:02.251 GCD[11764:1915764] 4------{number = 1, name = main}
2016-09-03 19:37:02.250 GCD[11764:1915884] 3------{number = 4, name = (null)}
2016-09-03 19:37:02.251 GCD[11764:1915885] 5------{number = 2, name = (null)}
從輸出結(jié)果中前邊的時(shí)間中可以看出,幾乎是同時(shí)遍歷的。
5. GCD的隊(duì)列組dispatch_group
有時(shí)候我們會(huì)有這樣的需求:分別異步執(zhí)行2個(gè)耗時(shí)操作,然后當(dāng)2個(gè)耗時(shí)操作都執(zhí)行完畢后再回到主線程執(zhí)行操作。這時(shí)候我們可以用到GCD的隊(duì)列組。
我們可以先把任務(wù)放到隊(duì)列中,然后將隊(duì)列放入隊(duì)列組中。
調(diào)用隊(duì)列組的dispatch_group_notify回到主線程執(zhí)行操作。
dispatch_group_t group =? dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程...
});
徹底學(xué)會(huì)多線程系列其他文章:
iOS多線程--徹底學(xué)會(huì)多線程之『pthread、NSThread』
iOS多線程--徹底學(xué)會(huì)多線程之『NSOperation』
iOS多線程--徹底學(xué)會(huì)多線程之『RunLoop』
作者:行走的少年郎
鏈接:http://www.lxweimin.com/p/2d57c72016c6
來源:簡(jiǎn)書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。