多線程【轉(zhuǎn)】

文章目錄

GCD簡介

任務(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簡介

什么是GCD呢沮明?我們先來看看百度百科的解釋簡單了解下概念

引自百度百科

Grand Central Dispatch(GCD) 是Apple開發(fā)的一個(gè)多核編程的較新的解決方法啸胧。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(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í)很簡單,只有兩步拘泞。

創(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)建對象田弥,需要傳入兩個(gè)參數(shù)涛酗,第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識符,用于DEBUG偷厦,可為空商叹;第二個(gè)參數(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ì)列,還可以使用dispatch_get_global_queue來創(chuàng)建全局并行隊(duì)列请唱。GCD默認(rèn)提供了全局的并行隊(duì)列弥咪,需要傳入兩個(gè)參數(shù)过蹂。第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級,一般用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ì)列

我們再來看看主隊(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)槲覀冊谥骶€程中執(zhí)行這段代碼魂仍。我們把任務(wù)放到了主隊(duì)列中拐辽,也就是放到了主線程的隊(duì)列中。而同步執(zhí)行有個(gè)特點(diǎn)擦酌,就是對于任務(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ù)都在等對方執(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è)簡單的東西——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

我們在創(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.reibang.com/p/2d57c72016c6

來源:簡書

著作權(quán)歸作者所有烘贴。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)禁添,非商業(yè)轉(zhuǎn)載請注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桨踪,一起剝皮案震驚了整個(gè)濱河市老翘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锻离,老刑警劉巖铺峭,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異汽纠,居然都是意外死亡卫键,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門虱朵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莉炉,“玉大人,你說我怎么就攤上這事碴犬⌒跄” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵服协,是天一觀的道長绍昂。 經(jīng)常有香客問我,道長蚯涮,這世上最難降的妖魔是什么治专? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮遭顶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泪蔫。我一直安慰自己棒旗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铣揉,像睡著了一般饶深。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逛拱,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天敌厘,我揣著相機(jī)與錄音,去河邊找鬼朽合。 笑死俱两,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的曹步。 我是一名探鬼主播宪彩,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼讲婚!你這毒婦竟也來了尿孔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤筹麸,失蹤者是張志新(化名)和其女友劉穎活合,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體物赶,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡白指,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了块差。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侵续。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖憨闰,靈堂內(nèi)的尸體忽然破棺而出状蜗,到底是詐尸還是另有隱情,我是刑警寧澤鹉动,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布轧坎,位于F島的核電站,受9級特大地震影響泽示,放射性物質(zhì)發(fā)生泄漏缸血。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一械筛、第九天 我趴在偏房一處隱蔽的房頂上張望捎泻。 院中可真熱鬧,春花似錦埋哟、人聲如沸笆豁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闯狱。三九已至煞赢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哄孤,已是汗流浹背照筑。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘦陈,地道東北人凝危。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像双饥,于是被迫代替她去往敵國和親媒抠。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. GCD簡介 什么是GCD呢咏花?我們先來看看百度百科的解釋簡單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 359評論 0 0
  • 本文首發(fā)于我的個(gè)人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「傳送門」[https...
    ITCharge閱讀 347,286評論 308 1,926
  • 孤獨(dú)是一個(gè)人的狂歡趴生,狂歡是一個(gè)人的寂寞。此話聽來很有意味深長的感覺昏翰,只有當(dāng)夜深人靜時(shí)苍匆,給孩子講完故事哄睡著后,才是...
    理療瑜伽邵閱讀 206評論 1 0
  • 寫在前面的話 從一個(gè)朋友那知道簡書的棚菊,下載軟件發(fā)現(xiàn)確實(shí)不錯(cuò)浸踩,于是就注冊了號。 今天是第一次發(fā)文章统求,想想就以wuli...
    小乖的爸爸閱讀 514評論 0 2