日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

GCD 详细讲解

發(fā)布時(shí)間:2023/12/18 编程问答 68 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GCD 详细讲解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. GCD 簡介

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

Grand Central Dispatch(GCD) 是 Apple 開發(fā)的一個(gè)多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并發(fā)任務(wù)。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。

為什么要用 GCD 呢?

因?yàn)?GCD 有很多好處啊,具體如下:

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. GCD 任務(wù)和隊(duì)列

學(xué)習(xí) GCD 之前,先來了解 GCD 中兩個(gè)核心概念:任務(wù)和隊(duì)列。

任務(wù):就是執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼。在 GCD 中是放在 block 中的。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行(sync)和異步執(zhí)行(async)。兩者的主要區(qū)別是:是否等待隊(duì)列的任務(wù)執(zhí)行結(jié)束,以及是否具備開啟新線程的能力。

同步添加任務(wù)到指定的隊(duì)列中,在添加的任務(wù)執(zhí)行結(jié)束之前,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行。

只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力。

異步添加任務(wù)到指定的隊(duì)列中,它不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù)。

可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力。

舉個(gè)簡單例子:你要打電話給小明和小白。

同步執(zhí)行就是,你打電話給小明的時(shí)候,不能同時(shí)打給小白,等到給小明打完了,才能打給小白(等待任務(wù)執(zhí)行結(jié)束)。而且只能用當(dāng)前的電話(不具備開啟新線程的能力)。

而異步執(zhí)行就是,你打電話給小明的時(shí)候,不等和小明通話結(jié)束,還能直接給小白打電話,不用等著和小明通話結(jié)束再打(不用等待任務(wù)執(zhí)行結(jié)束)。除了當(dāng)前電話,你還可以使用其他所能使用的電話(具備開啟新線程的能力)。

注意: 異步執(zhí)行(async) 雖然具有開啟新線程的能力,但是并不一定開啟新線程。這跟任務(wù)所指定的隊(duì)列類型有關(guān)(下面會(huì)講)。

隊(duì)列(Dispatch Queue):這里的隊(duì)列指執(zhí)行任務(wù)的等待隊(duì)列,即用來存放任務(wù)的隊(duì)列。隊(duì)列是一種特殊的線性表,采用 FIFO(先進(jìn)先出)的原則,即新任務(wù)總是被插入到隊(duì)列的末尾,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)。隊(duì)列的結(jié)構(gòu)可參考下圖:

在 GCD 中有兩種隊(duì)列:串行隊(duì)列和并發(fā)隊(duì)列。兩者都符合 FIFO(先進(jìn)先出)的原則。兩者的主要區(qū)別是:執(zhí)行順序不同,以及開啟線程數(shù)不同。

串行隊(duì)列(Serial Dispatch Queue):

每次只有一個(gè)任務(wù)被執(zhí)行。讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行。(只開啟一個(gè)線程,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))

并發(fā)隊(duì)列(Concurrent Dispatch Queue):

可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行。(可以開啟多個(gè)線程,并且同時(shí)執(zhí)行任務(wù))

注意:并發(fā)隊(duì)列 的并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效

兩者具體區(qū)別如下兩圖所示。

3. GCD 的使用步驟

GCD 的使用步驟其實(shí)很簡單,只有兩步。

創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列或并發(fā)隊(duì)列)

將任務(wù)追加到任務(wù)的等待隊(duì)列中,然后系統(tǒng)就會(huì)根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)

下邊來看看 隊(duì)列的創(chuàng)建方法/獲取方法,以及 任務(wù)的創(chuàng)建方法

3.1 隊(duì)列的創(chuàng)建方法/獲取方法

可以使用dispatch_queue_create來創(chuàng)建隊(duì)列,需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識符,用于 DEBUG,可為空,Dispatch Queue 的名稱推薦使用應(yīng)用程序 ID 這種逆序全程域名;第二個(gè)參數(shù)用來識別是串行隊(duì)列還是并發(fā)隊(duì)列。DISPATCH_QUEUE_SERIAL 表示串行隊(duì)列,DISPATCH_QUEUE_CONCURRENT 表示并發(fā)隊(duì)列。

//?串行隊(duì)列的創(chuàng)建方法dispatch_queue_t?queue?=?dispatch_queue_create(“net.bujige.testQueue”,?DISPATCH_QUEUE_SERIAL);//?并發(fā)隊(duì)列的創(chuàng)建方法dispatch_queue_t?queue?=?dispatch_queue_create(“net.bujige.testQueue”,?DISPATCH_QUEUE_CONCURRENT);

//?主隊(duì)列的獲取方法dispatch_queue_t?queue?=?dispatch_get_main_queue();

//?全局并發(fā)隊(duì)列的獲取方法dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);

3.2 任務(wù)的創(chuàng)建方法

GCD 提供了同步執(zhí)行任務(wù)的創(chuàng)建方法dispatch_sync和異步執(zhí)行任務(wù)創(chuàng)建方法dispatch_async。

//?同步執(zhí)行任務(wù)創(chuàng)建方法dispatch_sync(queue,?^{????//?這里放同步執(zhí)行任務(wù)代碼});//?異步執(zhí)行任務(wù)創(chuàng)建方法dispatch_async(queue,?^{????//?這里放異步執(zhí)行任務(wù)代碼});

雖然使用 GCD 只需兩步,但是既然我們有兩種隊(duì)列(串行隊(duì)列/并發(fā)隊(duì)列),兩種任務(wù)執(zhí)行方式(同步執(zhí)行/異步執(zhí)行),那么我們就有了四種不同的組合方式。這四種不同的組合方式是:

1.同步執(zhí)行 + 并發(fā)隊(duì)列

2.異步執(zhí)行 + 并發(fā)隊(duì)列

3.同步執(zhí)行 + 串行隊(duì)列

4.異步執(zhí)行 + 串行隊(duì)列

實(shí)際上,剛才還說了兩種特殊隊(duì)列:全局并發(fā)隊(duì)列、主隊(duì)列。全局并發(fā)隊(duì)列可以作為普通并發(fā)隊(duì)列來使用。但是主隊(duì)列因?yàn)橛悬c(diǎn)特殊,所以我們就又多了兩種組合方式。這樣就有六種不同的組合方式了。

5.同步執(zhí)行 + 主隊(duì)列

6.異步執(zhí)行 + 主隊(duì)列

那么這幾種不同組合方式各有什么區(qū)別呢,這里為了方便,先上結(jié)果,再來講解。你可以直接查看表格結(jié)果,然后跳過 4. GCD 的基本使用 。

下邊我們來分別講講這幾種不同的組合方式的使用方法。

4. GCD 的基本使用

先來講講并發(fā)隊(duì)列的兩種執(zhí)行方式。

4.1 同步執(zhí)行 + 并發(fā)隊(duì)列

/??同步執(zhí)行?+?并發(fā)隊(duì)列??特點(diǎn):在當(dāng)前線程中執(zhí)行任務(wù),不會(huì)開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。?/-?(void)syncConcurrent?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”syncConcurrent—begin”);????????dispatch_queue_t?queue?=?dispatch_queue_create(“net.bujige.testQueue”,?DISPATCH_QUEUE_CONCURRENT);????????dispatch_sync(queue,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:34:55.095932+0800 YSC-GCD-demo[19892:4996930] currentThread—{number = 1, name = main}

2018-02-23 20:34:55.096086+0800 YSC-GCD-demo[19892:4996930] syncConcurrent—begin

2018-02-23 20:34:57.097589+0800 YSC-GCD-demo[19892:4996930] 1—{number = 1, name = main}

2018-02-23 20:34:59.099100+0800 YSC-GCD-demo[19892:4996930] 1—{number = 1, name = main}

2018-02-23 20:35:01.099843+0800 YSC-GCD-demo[19892:4996930] 2—{number = 1, name = main}

2018-02-23 20:35:03.101171+0800 YSC-GCD-demo[19892:4996930] 2—{number = 1, name = main}

2018-02-23 20:35:05.101750+0800 YSC-GCD-demo[19892:4996930] 3—{number = 1, name = main}

2018-02-23 20:35:07.102414+0800 YSC-GCD-demo[19892:4996930] 3—{number = 1, name = main}

2018-02-23 20:35:07.102575+0800 YSC-GCD-demo[19892:4996930] syncConcurrent—end

從同步執(zhí)行 + 并發(fā)隊(duì)列中可看到:

所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的,沒有開啟新的線程(同步執(zhí)行不具備開啟新線程的能力)。

所有任務(wù)都在打印的syncConcurrent—begin和syncConcurrent—end之間執(zhí)行的(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。

任務(wù)按順序執(zhí)行的。按順序執(zhí)行的原因:雖然并發(fā)隊(duì)列可以開啟多個(gè)線程,并且同時(shí)執(zhí)行多個(gè)任務(wù)。但是因?yàn)楸旧聿荒軇?chuàng)建新線程,只有當(dāng)前線程這一個(gè)線程(同步任務(wù)不具備開啟新線程的能力),所以也就不存在并發(fā)。而且當(dāng)前線程只有等待當(dāng)前隊(duì)列中正在執(zhí)行的任務(wù)執(zhí)行完畢之后,才能繼續(xù)接著執(zhí)行下面的操作(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。所以任務(wù)只能一個(gè)接一個(gè)按順序執(zhí)行,不能同時(shí)被執(zhí)行。

4.2 異步執(zhí)行 + 并發(fā)隊(duì)列

可以開啟多個(gè)線程,任務(wù)交替(同時(shí))執(zhí)行。

/??異步執(zhí)行?+?并發(fā)隊(duì)列??特點(diǎn):可以開啟多個(gè)線程,任務(wù)交替(同時(shí))執(zhí)行。?/-?(void)asyncConcurrent?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”asyncConcurrent—begin”);????????dispatch_queue_t?queue?=?dispatch_queue_create(“net.bujige.testQueue”,?DISPATCH_QUEUE_CONCURRENT);????????dispatch_async(queue,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:36:41.769269+0800 YSC-GCD-demo[19929:5005237] currentThread—{number = 1, name = main}

2018-02-23 20:36:41.769496+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent—begin

2018-02-23 20:36:41.769725+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent—end

2018-02-23 20:36:43.774442+0800 YSC-GCD-demo[19929:5005566] 2—{number = 5, name = (null)}

2018-02-23 20:36:43.774440+0800 YSC-GCD-demo[19929:5005567] 3—{number = 4, name = (null)}

2018-02-23 20:36:43.774440+0800 YSC-GCD-demo[19929:5005565] 1—{number = 3, name = (null)}

2018-02-23 20:36:45.779286+0800 YSC-GCD-demo[19929:5005567] 3—{number = 4, name = (null)}

2018-02-23 20:36:45.779302+0800 YSC-GCD-demo[19929:5005565] 1—{number = 3, name = (null)}

2018-02-23 20:36:45.779286+0800 YSC-GCD-demo[19929:5005566] 2—{number = 5, name = (null)}

在異步執(zhí)行 + 并發(fā)隊(duì)列中可以看出:

除了當(dāng)前線程(主線程),系統(tǒng)又開啟了3個(gè)線程,并且任務(wù)是交替/同時(shí)執(zhí)行的。(異步執(zhí)行具備開啟新線程的能力。且并發(fā)隊(duì)列可開啟多個(gè)線程,同時(shí)執(zhí)行多個(gè)任務(wù))。

所有任務(wù)是在打印的syncConcurrent—begin和syncConcurrent—end之后才執(zhí)行的。說明當(dāng)前線程沒有等待,而是直接開啟了新線程,在新線程中執(zhí)行任務(wù)(異步執(zhí)行不做等待,可以繼續(xù)執(zhí)行任務(wù))。

接下來再來講講串行隊(duì)列的兩種執(zhí)行方式。

4.3 同步執(zhí)行 + 串行隊(duì)列

/??同步執(zhí)行?+?串行隊(duì)列??特點(diǎn):不會(huì)開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。?/-?(void)syncSerial?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”syncSerial—begin”);????????dispatch_queue_t?queue?=?dispatch_queue_create(“net.bujige.testQueue”,?DISPATCH_QUEUE_SERIAL);????????dispatch_sync(queue,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:39:37.876811+0800 YSC-GCD-demo[19975:5017162] currentThread—{number = 1, name = main}

2018-02-23 20:39:37.876998+0800 YSC-GCD-demo[19975:5017162] syncSerial—begin

2018-02-23 20:39:39.878316+0800 YSC-GCD-demo[19975:5017162] 1—{number = 1, name = main}

2018-02-23 20:39:41.879829+0800 YSC-GCD-demo[19975:5017162] 1—{number = 1, name = main}

2018-02-23 20:39:43.880660+0800 YSC-GCD-demo[19975:5017162] 2—{number = 1, name = main}

2018-02-23 20:39:45.881265+0800 YSC-GCD-demo[19975:5017162] 2—{number = 1, name = main}

2018-02-23 20:39:47.882257+0800 YSC-GCD-demo[19975:5017162] 3—{number = 1, name = main}

2018-02-23 20:39:49.883008+0800 YSC-GCD-demo[19975:5017162] 3—{number = 1, name = main}

2018-02-23 20:39:49.883253+0800 YSC-GCD-demo[19975:5017162] syncSerial—end

在同步執(zhí)行 + 串行隊(duì)列可以看到:

所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的,并沒有開啟新的線程(同步執(zhí)行不具備開啟新線程的能力)。

所有任務(wù)都在打印的syncConcurrent—begin和syncConcurrent—end之間執(zhí)行(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。

任務(wù)是按順序執(zhí)行的(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。

4.4 異步執(zhí)行 + 串行隊(duì)列

/??異步執(zhí)行?+?串行隊(duì)列??特點(diǎn):會(huì)開啟新線程,但是因?yàn)槿蝿?wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。?/-?(void)asyncSerial?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”asyncSerial—begin”);????????dispatch_queue_t?queue?=?dispatch_queue_create(“net.bujige.testQueue”,?DISPATCH_QUEUE_SERIAL);????????dispatch_async(queue,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:41:17.029999+0800 YSC-GCD-demo[20008:5024757] currentThread—{number = 1, name = main}

2018-02-23 20:41:17.030212+0800 YSC-GCD-demo[20008:5024757] asyncSerial—begin

2018-02-23 20:41:17.030364+0800 YSC-GCD-demo[20008:5024757] asyncSerial—end

2018-02-23 20:41:19.035379+0800 YSC-GCD-demo[20008:5024950] 1—{number = 3, name = (null)}

2018-02-23 20:41:21.037140+0800 YSC-GCD-demo[20008:5024950] 1—{number = 3, name = (null)}

2018-02-23 20:41:23.042220+0800 YSC-GCD-demo[20008:5024950] 2—{number = 3, name = (null)}

2018-02-23 20:41:25.042971+0800 YSC-GCD-demo[20008:5024950] 2—{number = 3, name = (null)}

2018-02-23 20:41:27.047690+0800 YSC-GCD-demo[20008:5024950] 3—{number = 3, name = (null)}

2018-02-23 20:41:29.052327+0800 YSC-GCD-demo[20008:5024950] 3—{number = 3, name = (null)}

在異步執(zhí)行 + 串行隊(duì)列可以看到:

開啟了一條新線程(異步執(zhí)行具備開啟新線程的能力,串行隊(duì)列只開啟一個(gè)線程)。

所有任務(wù)是在打印的syncConcurrent—begin和syncConcurrent—end之后才開始執(zhí)行的(異步執(zhí)行不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù))。

任務(wù)是按順序執(zhí)行的(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。

下邊講講剛才我們提到過的特殊隊(duì)列:主隊(duì)列

我們再來看看主隊(duì)列的兩種組合方式。

4.5 同步執(zhí)行 + 主隊(duì)列

同步執(zhí)行 + 主隊(duì)列在不同線程中調(diào)用結(jié)果也是不一樣,在主線程中調(diào)用會(huì)出現(xiàn)死鎖,而在其他線程中則不會(huì)。

4.5.1 在主線程中調(diào)用同步執(zhí)行 + 主隊(duì)列

/??同步執(zhí)行?+?主隊(duì)列??特點(diǎn)(主線程調(diào)用):互等卡主不執(zhí)行。??特點(diǎn)(其他線程調(diào)用):不會(huì)開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。?/-?(void)syncMain?{????????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”syncMain—begin”);????????dispatch_queue_t?queue?=?dispatch_get_main_queue();????????dispatch_sync(queue,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:42:36.842892+0800 YSC-GCD-demo[20041:5030982] currentThread—{number = 1, name = main}

2018-02-23 20:42:36.843050+0800 YSC-GCD-demo[20041:5030982] syncMain—begin

(lldb)

在同步執(zhí)行 + 主隊(duì)列可以驚奇的發(fā)現(xiàn):

這是因?yàn)槲覀冊谥骶€程中執(zhí)行syncMain方法,相當(dāng)于把syncMain任務(wù)放到了主線程的隊(duì)列中。而同步執(zhí)行會(huì)等待當(dāng)前隊(duì)列中的任務(wù)執(zhí)行完畢,才會(huì)接著執(zhí)行。那么當(dāng)我們把任務(wù)1追加到主隊(duì)列中,任務(wù)1就在等待主線程處理完syncMain任務(wù)。而syncMain任務(wù)需要等待任務(wù)1執(zhí)行完畢,才能接著執(zhí)行。

那么,現(xiàn)在的情況就是syncMain任務(wù)和任務(wù)1都在等對方執(zhí)行完畢。這樣大家互相等待,所以就卡住了,所以我們的任務(wù)執(zhí)行不了,而且syncMain—end也沒有打印。

要是如果不在主線程中調(diào)用,而在其他線程中調(diào)用會(huì)如何呢?

4.5.2 在其他線程中調(diào)用同步執(zhí)行 + 主隊(duì)列

//?使用?NSThread?的?detachNewThreadSelector?方法會(huì)創(chuàng)建線程,并自動(dòng)啟動(dòng)線程執(zhí)行?selector?任務(wù)[NSThread?detachNewThreadSelector:@selector(syncMain)?toTarget:self?withObject:nil];

輸出結(jié)果:

2018-02-23 20:44:19.377321+0800 YSC-GCD-demo[20083:5040347] currentThread—{number = 3, name = (null)}

2018-02-23 20:44:19.377494+0800 YSC-GCD-demo[20083:5040347] syncMain—begin

2018-02-23 20:44:21.384716+0800 YSC-GCD-demo[20083:5040132] 1—{number = 1, name = main}

2018-02-23 20:44:23.386091+0800 YSC-GCD-demo[20083:5040132] 1—{number = 1, name = main}

2018-02-23 20:44:25.387687+0800 YSC-GCD-demo[20083:5040132] 2—{number = 1, name = main}

2018-02-23 20:44:27.388648+0800 YSC-GCD-demo[20083:5040132] 2—{number = 1, name = main}

2018-02-23 20:44:29.390459+0800 YSC-GCD-demo[20083:5040132] 3—{number = 1, name = main}

2018-02-23 20:44:31.391965+0800 YSC-GCD-demo[20083:5040132] 3—{number = 1, name = main}

2018-02-23 20:44:31.392513+0800 YSC-GCD-demo[20083:5040347] syncMain—end

在其他線程中使用同步執(zhí)行 + 主隊(duì)列可看到:

所有任務(wù)都是在主線程(非當(dāng)前線程)中執(zhí)行的,沒有開啟新的線程(所有放在主隊(duì)列中的任務(wù),都會(huì)放到主線程中執(zhí)行)。

所有任務(wù)都在打印的syncConcurrent—begin和syncConcurrent—end之間執(zhí)行(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。

任務(wù)是按順序執(zhí)行的(主隊(duì)列是串行隊(duì)列,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。

為什么現(xiàn)在就不會(huì)卡住了呢?

因?yàn)閟yncMain 任務(wù)放到了其他線程里,而任務(wù)1、任務(wù)2、任務(wù)3都在追加到主隊(duì)列中,這三個(gè)任務(wù)都會(huì)在主線程中執(zhí)行。syncMain 任務(wù)在其他線程中執(zhí)行到追加任務(wù)1到主隊(duì)列中,因?yàn)橹麝?duì)列現(xiàn)在沒有正在執(zhí)行的任務(wù),所以,會(huì)直接執(zhí)行主隊(duì)列的任務(wù)1,等任務(wù)1執(zhí)行完畢,再接著執(zhí)行任務(wù)2、任務(wù)3。所以這里不會(huì)卡住線程。

4.6 異步執(zhí)行 + 主隊(duì)列

/??異步執(zhí)行?+?主隊(duì)列??特點(diǎn):只在主線程中執(zhí)行任務(wù),執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)?/-?(void)asyncMain?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”asyncMain—begin”);????????dispatch_queue_t?queue?=?dispatch_get_main_queue();????????dispatch_async(queue,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:45:49.981505+0800 YSC-GCD-demo[20111:5046708] currentThread—{number = 1, name = main}

2018-02-23 20:45:49.981935+0800 YSC-GCD-demo[20111:5046708] asyncMain—begin

2018-02-23 20:45:49.982352+0800 YSC-GCD-demo[20111:5046708] asyncMain—end

2018-02-23 20:45:51.991096+0800 YSC-GCD-demo[20111:5046708] 1—{number = 1, name = main}

2018-02-23 20:45:53.991959+0800 YSC-GCD-demo[20111:5046708] 1—{number = 1, name = main}

2018-02-23 20:45:55.992937+0800 YSC-GCD-demo[20111:5046708] 2—{number = 1, name = main}

2018-02-23 20:45:57.993649+0800 YSC-GCD-demo[20111:5046708] 2—{number = 1, name = main}

2018-02-23 20:45:59.994928+0800 YSC-GCD-demo[20111:5046708] 3—{number = 1, name = main}

2018-02-23 20:46:01.995589+0800 YSC-GCD-demo[20111:5046708] 3—{number = 1, name = main}

在異步執(zhí)行 + 主隊(duì)列可以看到:

所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的,并沒有開啟新的線程(雖然異步執(zhí)行具備開啟線程的能力,但因?yàn)槭侵麝?duì)列,所以所有任務(wù)都在主線程中)。

所有任務(wù)是在打印的syncConcurrent—begin和syncConcurrent—end之后才開始執(zhí)行的(異步執(zhí)行不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù))。

任務(wù)是按順序執(zhí)行的(因?yàn)橹麝?duì)列是串行隊(duì)列,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。

弄懂了難理解、繞來繞去的隊(duì)列+任務(wù)之后,我們來學(xué)習(xí)一個(gè)簡單的東西:5. GCD 線程間的通信。

5. GCD 線程間的通信

在 iOS 開發(fā)過程中,我們一般在主線程里邊進(jìn)行 UI 刷新,例如:點(diǎn)擊、滾動(dòng)、拖拽等事件。我們通常把一些耗時(shí)的操作放在其他線程,比如說圖片下載、文件上傳等耗時(shí)操作。而當(dāng)我們有時(shí)候在其他線程完成了耗時(shí)操作時(shí),需要回到主線程,那么就用到了線程之間的通訊。

/??線程間通信?/-?(void)communication?{????//?獲取全局并發(fā)隊(duì)列????dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);?????//?獲取主隊(duì)列????dispatch_queue_t?mainQueue?=?dispatch_get_main_queue();?????????dispatch_async(queue,?^{????????//?異步追加任務(wù)????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:47:03.462394+0800 YSC-GCD-demo[20154:5053282] 1—{number = 3, name = (null)}

2018-02-23 20:47:05.465912+0800 YSC-GCD-demo[20154:5053282] 1—{number = 3, name = (null)}

2018-02-23 20:47:07.466657+0800 YSC-GCD-demo[20154:5052953] 2—{number = 1, name = main}

6. GCD 的其他方法

6.1 GCD 柵欄方法:dispatch_barrier_async

我們有時(shí)需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于 柵欄 一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來,當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄。

dispatch_barrier_async函數(shù)會(huì)等待前邊追加到并發(fā)隊(duì)列中的任務(wù)全部執(zhí)行完畢之后,再將指定的任務(wù)追加到該異步隊(duì)列中。然后在dispatch_barrier_async函數(shù)追加的任務(wù)執(zhí)行完畢之后,異步隊(duì)列才恢復(fù)為一般動(dòng)作,接著追加任務(wù)到該異步隊(duì)列并開始執(zhí)行。具體如下圖所示:

/??柵欄方法?dispatch_barrier_async?/-?(void)barrier?{????dispatch_queue_t?queue?=?dispatch_queue_create(“net.bujige.testQueue”,?DISPATCH_QUEUE_CONCURRENT);????????dispatch_async(queue,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 20:48:18.297745+0800 YSC-GCD-demo[20188:5059274] 1—{number = 4, name = (null)}

2018-02-23 20:48:18.297745+0800 YSC-GCD-demo[20188:5059273] 2—{number = 3, name = (null)}

2018-02-23 20:48:20.301139+0800 YSC-GCD-demo[20188:5059274] 1—{number = 4, name = (null)}

2018-02-23 20:48:20.301139+0800 YSC-GCD-demo[20188:5059273] 2—{number = 3, name = (null)}

2018-02-23 20:48:22.306290+0800 YSC-GCD-demo[20188:5059274] barrier—{number = 4, name = (null)}

2018-02-23 20:48:24.311655+0800 YSC-GCD-demo[20188:5059274] barrier—{number = 4, name = (null)}

2018-02-23 20:48:26.316943+0800 YSC-GCD-demo[20188:5059273] 4—{number = 3, name = (null)}

2018-02-23 20:48:26.316956+0800 YSC-GCD-demo[20188:5059274] 3—{number = 4, name = (null)}

2018-02-23 20:48:28.320660+0800 YSC-GCD-demo[20188:5059273] 4—{number = 3, name = (null)}

2018-02-23 20:48:28.320649+0800 YSC-GCD-demo[20188:5059274] 3—{number = 4, name = (null)}

在dispatch_barrier_async相關(guān)代碼執(zhí)行結(jié)果中可以看出:

6.2 GCD 延時(shí)執(zhí)行方法:dispatch_after

我們經(jīng)常會(huì)遇到這樣的需求:在指定時(shí)間(例如3秒)之后執(zhí)行某個(gè)任務(wù)。可以用 GCD 的dispatch_after函數(shù)來實(shí)現(xiàn)。

需要注意的是:dispatch_after函數(shù)并不是在指定時(shí)間之后才開始執(zhí)行處理,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中。嚴(yán)格來說,這個(gè)時(shí)間并不是絕對準(zhǔn)確的,但想要大致延遲執(zhí)行任務(wù),dispatch_after函數(shù)是很有效的。

/??延時(shí)執(zhí)行方法?dispatch_after?/-?(void)after?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”asyncMain—begin”);????????dispatch_after(dispatch_time(DISPATCH_TIME_NOW,?(int64_t)(2.0??NSEC_PER_SEC)),?dispatch_get_main_queue(),?^{????????//?2.0秒后異步追加任務(wù)代碼到主隊(duì)列,并開始執(zhí)行????????NSLog(@”after—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????});}

輸出結(jié)果:

2018-02-23 20:53:08.713784+0800 YSC-GCD-demo[20282:5080295] currentThread—{number = 1, name = main}

2018-02-23 20:53:08.713962+0800 YSC-GCD-demo[20282:5080295] asyncMain—begin

2018-02-23 20:53:10.714283+0800 YSC-GCD-demo[20282:5080295] after—{number = 1, name = main}

在dispatch_after相關(guān)代碼執(zhí)行結(jié)果中可以看出:在打印 asyncMain—begin 之后大約 2.0 秒的時(shí)間,打印了 after—{number = 1, name = main}

6.3 GCD 一次性代碼(只執(zhí)行一次):dispatch_once

我們在創(chuàng)建單例、或者有整個(gè)程序運(yùn)行過程中只執(zhí)行一次的代碼時(shí),我們就用到了 GCD 的 dispatch_once 函數(shù)。使用dispatch_once 函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次,并且即使在多線程的環(huán)境下,dispatch_once也可以保證線程安全。

/??一次性代碼(只執(zhí)行一次)dispatch_once?/-?(void)once?{????static?dispatch_once_t?onceToken;????dispatch_once(&onceToken,?^{????????//?只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)????});}

6.4 GCD 快速迭代方法:dispatch_apply

我們可以利用異步隊(duì)列同時(shí)遍歷。比如說遍歷 0~5 這6個(gè)數(shù)字,for 循環(huán)的做法是每次取出一個(gè)元素,逐個(gè)遍歷。dispatch_apply可以同時(shí)遍歷多個(gè)數(shù)字。

/??快速迭代方法?dispatch_apply?/-?(void)apply?{????dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);????????NSLog(@”apply—begin”);????dispatch_apply(6,?queue,?^(size_t?index)?{????????NSLog(@”%zd—%@”,index,?[NSThread?currentThread]);????});????NSLog(@”apply—end”);}

輸出結(jié)果:

2018-02-23 22:03:18.475499+0800 YSC-GCD-demo[20470:5176805] apply—begin

2018-02-23 22:03:18.476672+0800 YSC-GCD-demo[20470:5177035] 1—{number = 3, name = (null)}

2018-02-23 22:03:18.476693+0800 YSC-GCD-demo[20470:5176805] 0—{number = 1, name = main}

2018-02-23 22:03:18.476704+0800 YSC-GCD-demo[20470:5177037] 2—{number = 4, name = (null)}

2018-02-23 22:03:18.476735+0800 YSC-GCD-demo[20470:5177036] 3—{number = 5, name = (null)}

2018-02-23 22:03:18.476867+0800 YSC-GCD-demo[20470:5177035] 4—{number = 3, name = (null)}

2018-02-23 22:03:18.476867+0800 YSC-GCD-demo[20470:5176805] 5—{number = 1, name = main}

2018-02-23 22:03:18.477038+0800 YSC-GCD-demo[20470:5176805] apply—end

從dispatch_apply相關(guān)代碼執(zhí)行結(jié)果中可以看出:

因?yàn)槭窃诓l(fā)隊(duì)列中異步隊(duì)執(zhí)行任務(wù),所以各個(gè)任務(wù)的執(zhí)行時(shí)間長短不定,最后結(jié)束順序也不定。但是apply—end一定在最后執(zhí)行。這是因?yàn)閐ispatch_apply函數(shù)會(huì)等待全部任務(wù)執(zhí)行完畢。

6.5 GCD 的隊(duì)列組:dispatch_group

有時(shí)候我們會(huì)有這樣的需求:分別異步執(zhí)行2個(gè)耗時(shí)任務(wù),然后當(dāng)2個(gè)耗時(shí)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。這時(shí)候我們可以用到 GCD 的隊(duì)列組。

調(diào)用隊(duì)列組的 dispatch_group_async 先把任務(wù)放到隊(duì)列中,然后將隊(duì)列放入隊(duì)列組中。或者使用隊(duì)列組的 dispatch_group_enter、dispatch_group_leave 組合 來實(shí)現(xiàn)dispatch_group_async。

調(diào)用隊(duì)列組的 dispatch_group_notify 回到指定線程執(zhí)行任務(wù)。或者使用 dispatch_group_wait 回到當(dāng)前線程繼續(xù)向下執(zhí)行(會(huì)阻塞當(dāng)前線程)。

6.5.1 dispatch_group_notify

/??隊(duì)列組?dispatch_group_notify?/-?(void)groupNotify?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”group—begin”);????????dispatch_group_t?group?=??dispatch_group_create();????????dispatch_group_async(group,?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 22:05:03.790035+0800 YSC-GCD-demo[20494:5183349] currentThread—{number = 1, name = main}

2018-02-23 22:05:03.790237+0800 YSC-GCD-demo[20494:5183349] group—begin

2018-02-23 22:05:05.792721+0800 YSC-GCD-demo[20494:5183654] 1—{number = 4, name = (null)}

2018-02-23 22:05:05.792725+0800 YSC-GCD-demo[20494:5183656] 2—{number = 3, name = (null)}

2018-02-23 22:05:07.797408+0800 YSC-GCD-demo[20494:5183656] 2—{number = 3, name = (null)}

2018-02-23 22:05:07.797408+0800 YSC-GCD-demo[20494:5183654] 1—{number = 4, name = (null)}

2018-02-23 22:05:09.798717+0800 YSC-GCD-demo[20494:5183349] 3—{number = 1, name = main}

2018-02-23 22:05:11.799827+0800 YSC-GCD-demo[20494:5183349] 3—{number = 1, name = main}

2018-02-23 22:05:11.799977+0800 YSC-GCD-demo[20494:5183349] group—end

從dispatch_group_notify相關(guān)代碼運(yùn)行輸出結(jié)果可以看出:

當(dāng)所有任務(wù)都執(zhí)行完成之后,才執(zhí)行dispatch_group_notify block 中的任務(wù)。

6.5.2 dispatch_group_wait

/??隊(duì)列組?dispatch_group_wait?/-?(void)groupWait?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”group—begin”);????????dispatch_group_t?group?=??dispatch_group_create();????????dispatch_group_async(group,?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 22:10:16.939258+0800 YSC-GCD-demo[20538:5198871] currentThread—{number = 1, name = main}

2018-02-23 22:10:16.939455+0800 YSC-GCD-demo[20538:5198871] group—begin

2018-02-23 22:10:18.943862+0800 YSC-GCD-demo[20538:5199137] 2—{number = 4, name = (null)}

2018-02-23 22:10:18.943861+0800 YSC-GCD-demo[20538:5199138] 1—{number = 3, name = (null)}

2018-02-23 22:10:20.947787+0800 YSC-GCD-demo[20538:5199137] 2—{number = 4, name = (null)}

2018-02-23 22:10:20.947790+0800 YSC-GCD-demo[20538:5199138] 1—{number = 3, name = (null)}

2018-02-23 22:10:20.948134+0800 YSC-GCD-demo[20538:5198871] group—end

從dispatch_group_wait相關(guān)代碼運(yùn)行輸出結(jié)果可以看出:

當(dāng)所有任務(wù)執(zhí)行完成之后,才執(zhí)行 dispatch_group_wait 之后的操作。但是,使用dispatch_group_wait 會(huì)阻塞當(dāng)前線程。

6.5.3 dispatch_group_enter、dispatch_group_leave

dispatch_group_enter 標(biāo)志著一個(gè)任務(wù)追加到 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)+1

dispatch_group_leave 標(biāo)志著一個(gè)任務(wù)離開了 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)-1。

當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0的時(shí)候,才會(huì)使dispatch_group_wait解除阻塞,以及執(zhí)行追加到dispatch_group_notify中的任務(wù)。

/??隊(duì)列組?dispatch_group_enter、dispatch_group_leave?/-?(void)groupEnterAndLeave{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”group—begin”);????????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,?^{????????//?追加任務(wù)1????????for?(int?i?=?0;?i?

輸出結(jié)果:

2018-02-23 22:14:17.997667+0800 YSC-GCD-demo[20592:5214830] currentThread—{number = 1, name = main}

2018-02-23 22:14:17.997839+0800 YSC-GCD-demo[20592:5214830] group—begin

2018-02-23 22:14:20.000298+0800 YSC-GCD-demo[20592:5215094] 1—{number = 4, name = (null)}

2018-02-23 22:14:20.000305+0800 YSC-GCD-demo[20592:5215095] 2—{number = 3, name = (null)}

2018-02-23 22:14:22.001323+0800 YSC-GCD-demo[20592:5215094] 1—{number = 4, name = (null)}

2018-02-23 22:14:22.001339+0800 YSC-GCD-demo[20592:5215095] 2—{number = 3, name = (null)}

2018-02-23 22:14:24.002321+0800 YSC-GCD-demo[20592:5214830] 3—{number = 1, name = main}

2018-02-23 22:14:26.002852+0800 YSC-GCD-demo[20592:5214830] 3—{number = 1, name = main}

2018-02-23 22:14:26.003116+0800 YSC-GCD-demo[20592:5214830] group—end

從dispatch_group_enter、dispatch_group_leave相關(guān)代碼運(yùn)行結(jié)果中可以看出:當(dāng)所有任務(wù)執(zhí)行完成之后,才執(zhí)行 dispatch_group_notify 中的任務(wù)。這里的dispatch_group_enter、dispatch_group_leave組合,其實(shí)等同于dispatch_group_async。

6.6 GCD 信號量:dispatch_semaphore

GCD 中的信號量是指 Dispatch Semaphore,是持有計(jì)數(shù)的信號。類似于過高速路收費(fèi)站的欄桿。可以通過時(shí),打開欄桿,不可以通過時(shí),關(guān)閉欄桿。在 Dispatch Semaphore 中,使用計(jì)數(shù)來完成這個(gè)功能,計(jì)數(shù)為0時(shí)等待,不可通過。計(jì)數(shù)為1或大于1時(shí),計(jì)數(shù)減1且不等待,可通過。Dispatch Semaphore 提供了三個(gè)函數(shù)。

dispatch_semaphore_create:創(chuàng)建一個(gè)Semaphore并初始化信號的總量

dispatch_semaphore_signal:發(fā)送一個(gè)信號,讓信號總量加1

dispatch_semaphore_wait:可以使總信號量減1,當(dāng)信號總量為0時(shí)就會(huì)一直等待(阻塞所在線程),否則就可以正常執(zhí)行。

注意:信號量的使用前提是:想清楚你需要處理哪個(gè)線程等待(阻塞),又要哪個(gè)線程繼續(xù)執(zhí)行,然后使用信號量。

Dispatch Semaphore 在實(shí)際開發(fā)中主要用于:

6.6.1 Dispatch Semaphore 線程同步

我們在開發(fā)中,會(huì)遇到這樣的需求:異步執(zhí)行耗時(shí)任務(wù),并使用異步執(zhí)行的結(jié)果進(jìn)行一些額外的操作。換句話說,相當(dāng)于,將將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。比如說:AFNetworking 中 AFURLSessionManager.m 里面的 tasksForKeyPath: 方法。通過引入信號量的方式,等待異步執(zhí)行任務(wù)結(jié)果,獲取到 tasks,然后再返回該 tasks。

-?(NSArray?)tasksForKeyPath:(NSString?)keyPath?{????__block?NSArray?tasks?=?nil;????dispatch_semaphore_t?semaphore?=?dispatch_semaphore_create(0);????[self.session?getTasksWithCompletionHandler:^(NSArray?*dataTasks,?NSArray?*uploadTasks,?NSArray?*downloadTasks)?{????????if?([keyPath?isEqualToString:NSStringFromSelector(@selector(dataTasks))])?{????????????tasks?=?dataTasks;????????}?else?if?([keyPath?isEqualToString:NSStringFromSelector(@selector(uploadTasks))])?{????????????tasks?=?uploadTasks;????????}?else?if?([keyPath?isEqualToString:NSStringFromSelector(@selector(downloadTasks))])?{????????????tasks?=?downloadTasks;????????}?else?if?([keyPath?isEqualToString:NSStringFromSelector(@selector(tasks))])?{????????????tasks?=?[@[dataTasks,?uploadTasks,?downloadTasks]?valueForKeyPath:@”@unionOfArrays.self”];????????}????????dispatch_semaphore_signal(semaphore);????}];????dispatch_semaphore_wait(semaphore,?DISPATCH_TIME_FOREVER);????return?tasks;}

下面,我們來利用 Dispatch Semaphore 實(shí)現(xiàn)線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。

/??semaphore?線程同步?/-?(void)semaphoreSync?{????????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”semaphore—begin”);????????dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);????dispatch_semaphore_t?semaphore?=?dispatch_semaphore_create(0);????????__block?int?number?=?0;????dispatch_async(queue,?^{????????//?追加任務(wù)1????????[NSThread?sleepForTimeInterval:2];??????????????//?模擬耗時(shí)操作????????NSLog(@”1—%@”,[NSThread?currentThread]);??????//?打印當(dāng)前線程????????????????number?=?100;????????????????dispatch_semaphore_signal(semaphore);????});????????dispatch_semaphore_wait(semaphore,?DISPATCH_TIME_FOREVER);????NSLog(@”semaphore—end,number?=?%zd”,number);}

輸出結(jié)果:

2018-02-23 22:22:26.521665+0800 YSC-GCD-demo[20642:5246341] currentThread—{number = 1, name = main}

2018-02-23 22:22:26.521869+0800 YSC-GCD-demo[20642:5246341] semaphore—begin

2018-02-23 22:22:28.526841+0800 YSC-GCD-demo[20642:5246638] 1—{number = 3, name = (null)}

2018-02-23 22:22:28.527030+0800 YSC-GCD-demo[20642:5246341] semaphore—end,number = 100

從 Dispatch Semaphore 實(shí)現(xiàn)線程同步的代碼可以看到:

semaphore—end 是在執(zhí)行完 ?number = 100; 之后才打印的。而且輸出結(jié)果 number 為 100。這是因?yàn)楫惒綀?zhí)行不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù)。異步執(zhí)行將任務(wù)1追加到隊(duì)列之后,不做等待,接著執(zhí)行dispatch_semaphore_wait方法。此時(shí) semaphore == 0,當(dāng)前線程進(jìn)入等待狀態(tài)。然后,異步任務(wù)1開始執(zhí)行。任務(wù)1執(zhí)行到dispatch_semaphore_signal之后,總信號量,此時(shí) semaphore == 1,dispatch_semaphore_wait方法使總信號量減1,正在被阻塞的線程(主線程)恢復(fù)繼續(xù)執(zhí)行。最后打印semaphore—end,number = 100。這樣就實(shí)現(xiàn)了線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。

6.6.2 Dispatch Semaphore 線程安全和線程同步(為線程加鎖)

線程安全:如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。

若每個(gè)線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作(更改變量),一般都需要考慮線程同步,否則的話就可能影響線程安全。

線程同步:可理解為線程 A 和 線程 B 一塊配合,A 執(zhí)行到一定程度時(shí)要依靠線程 B 的某個(gè)結(jié)果,于是停下來,示意 B 運(yùn)行;B 依言執(zhí)行,再將結(jié)果給 A;A 再繼續(xù)操作。

舉個(gè)簡單例子就是:兩個(gè)人在一起聊天。兩個(gè)人不能同時(shí)說話,避免聽不清(操作沖突)。等一個(gè)人說完(一個(gè)線程結(jié)束操作),另一個(gè)再說(另一個(gè)線程再開始操作)。

下面,我們模擬火車票售賣的方式,實(shí)現(xiàn) NSThread 線程安全和解決線程同步問題。

場景:總共有50張火車票,有兩個(gè)售賣火車票的窗口,一個(gè)是北京火車票售賣窗口,另一個(gè)是上海火車票售賣窗口。兩個(gè)窗口同時(shí)售賣火車票,賣完為止。

6.6.2.1 非線程安全(不使用 semaphore)

先來看看不考慮線程安全的代碼:

/??非線程安全:不使用?semaphore??初始化火車票數(shù)量、賣票窗口(非線程安全)、并開始賣票?/-?(void)initTicketStatusNotSave?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”semaphore—begin”);????????self.ticketSurplusCount?=?50;????????//?queue1?代表北京火車票售賣窗口????dispatch_queue_t?queue1?=?dispatch_queue_create(“net.bujige.testQueue1”,?DISPATCH_QUEUE_SERIAL);????//?queue2?代表上海火車票售賣窗口????dispatch_queue_t?queue2?=?dispatch_queue_create(“net.bujige.testQueue2”,?DISPATCH_QUEUE_SERIAL);????????__weak?typeof(self)?weakSelf?=?self;????dispatch_async(queue1,?^{????????[weakSelf?saleTicketNotSafe];????});????????dispatch_async(queue2,?^{????????[weakSelf?saleTicketNotSafe];????});}/??售賣火車票(非線程安全)?/-?(void)saleTicketNotSafe?{????while?(1)?{????????????????if?(self.ticketSurplusCount?>?0)?{??//如果還有票,繼續(xù)售賣????????????self.ticketSurplusCount–;????????????NSLog(@”%@”,?[NSString?stringWithFormat:@”剩余票數(shù):%d?窗口:%@”,?self.ticketSurplusCount,?[NSThread?currentThread]]);????????????[NSThread?sleepForTimeInterval:0.2];????????}?else?{?//如果已賣完,關(guān)閉售票窗口????????????NSLog(@”所有火車票均已售完”);????????????break;????????}????????????}}

輸出結(jié)果(部分):

2018-02-23 22:25:35.789072+0800 YSC-GCD-demo[20712:5258914] currentThread—{number = 1, name = main}

2018-02-23 22:25:35.789260+0800 YSC-GCD-demo[20712:5258914] semaphore—begin

2018-02-23 22:25:35.789641+0800 YSC-GCD-demo[20712:5259176] 剩余票數(shù):48 窗口:{number = 3, name = (null)}

2018-02-23 22:25:35.789646+0800 YSC-GCD-demo[20712:5259175] 剩余票數(shù):49 窗口:{number = 4, name = (null)}

2018-02-23 22:25:35.994113+0800 YSC-GCD-demo[20712:5259175] 剩余票數(shù):47 窗口:{number = 4, name = (null)}

2018-02-23 22:25:35.994129+0800 YSC-GCD-demo[20712:5259176] 剩余票數(shù):46 窗口:{number = 3, name = (null)}

2018-02-23 22:25:36.198993+0800 YSC-GCD-demo[20712:5259176] 剩余票數(shù):45 窗口:{number = 3, name = (null)}

……

可以看到在不考慮線程安全,不使用 semaphore 的情況下,得到票數(shù)是錯(cuò)亂的,這樣顯然不符合我們的需求,所以我們需要考慮線程安全問題。

6.6.2.2 線程安全(使用 semaphore 加鎖)

考慮線程安全的代碼:

/??線程安全:使用?semaphore?加鎖??初始化火車票數(shù)量、賣票窗口(線程安全)、并開始賣票?/-?(void)initTicketStatusSave?{????NSLog(@”currentThread—%@”,[NSThread?currentThread]);??//?打印當(dāng)前線程????NSLog(@”semaphore—begin”);????????semaphoreLock?=?dispatch_semaphore_create(1);????????self.ticketSurplusCount?=?50;????????//?queue1?代表北京火車票售賣窗口????dispatch_queue_t?queue1?=?dispatch_queue_create(“net.bujige.testQueue1”,?DISPATCH_QUEUE_SERIAL);????//?queue2?代表上海火車票售賣窗口????dispatch_queue_t?queue2?=?dispatch_queue_create(“net.bujige.testQueue2”,?DISPATCH_QUEUE_SERIAL);????????__weak?typeof(self)?weakSelf?=?self;????dispatch_async(queue1,?^{????????[weakSelf?saleTicketSafe];????});????????dispatch_async(queue2,?^{????????[weakSelf?saleTicketSafe];????});}/*??售賣火車票(線程安全)?/-?(void)saleTicketSafe?{????while?(1)?{????????//?相當(dāng)于加鎖????????dispatch_semaphore_wait(semaphoreLock,?DISPATCH_TIME_FOREVER);????????????????if?(self.ticketSurplusCount?>?0)?{??//如果還有票,繼續(xù)售賣????????????self.ticketSurplusCount–;????????????NSLog(@”%@”,?[NSString?stringWithFormat:@”剩余票數(shù):%d?窗口:%@”,?self.ticketSurplusCount,?[NSThread?currentThread]]);????????????[NSThread?sleepForTimeInterval:0.2];????????}?else?{?//如果已賣完,關(guān)閉售票窗口????????????NSLog(@”所有火車票均已售完”);????????????????????????//?相當(dāng)于解鎖????????????dispatch_semaphore_signal(semaphoreLock);????????????break;????????}????????????????//?相當(dāng)于解鎖????????dispatch_semaphore_signal(semaphoreLock);????}}

輸出結(jié)果為:

2018-02-23 22:32:19.814232+0800 YSC-GCD-demo[20862:5290531] currentThread—{number = 1, name = main}

2018-02-23 22:32:19.814412+0800 YSC-GCD-demo[20862:5290531] semaphore—begin

2018-02-23 22:32:19.814837+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):49 窗口:{number = 3, name = (null)}

2018-02-23 22:32:20.017745+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):48 窗口:{number = 4, name = (null)}

2018-02-23 22:32:20.222039+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):47 窗口:{number = 3, name = (null)}

2018-02-23 22:32:29.024817+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):4 窗口:{number = 4, name = (null)}

2018-02-23 22:32:29.230110+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):3 窗口:{number = 3, name = (null)}

2018-02-23 22:32:29.433615+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):2 窗口:{number = 4, name = (null)}

2018-02-23 22:32:29.637572+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):1 窗口:{number = 3, name = (null)}

2018-02-23 22:32:29.840234+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):0 窗口:{number = 4, name = (null)}

2018-02-23 22:32:30.044960+0800 YSC-GCD-demo[20862:5290687] 所有火車票均已售完

2018-02-23 22:32:30.045260+0800 YSC-GCD-demo[20862:5290689] 所有火車票均已售完

可以看出,在考慮了線程安全的情況下,使用?dispatch_semaphore?機(jī)制之后,得到的票數(shù)是正確的,沒有出現(xiàn)混亂的情況。我們也就解決了多個(gè)線程同步的問題。

總結(jié)

以上是生活随笔為你收集整理的GCD 详细讲解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

欧美在线日韩在线 | 99自拍视频在线观看 | 97视频在线观看免费 | 久久久久久久久久久电影 | 国内偷拍精品视频 | 欧美91精品国产自产 | av激情五月 | 国产精品自产拍在线观看中文 | 午夜精品久久久久久久99婷婷 | 六月激情久久 | 国产一级免费观看视频 | 欧洲精品视频一区 | 人人澡人 | 久久精品久久99 | 久久国产精品精品国产色婷婷 | 亚洲婷婷在线 | www麻豆视频 | 亚洲爱av| 亚洲欧美怡红院 | 中文字幕人成乱码在线观看 | 免费日韩在线 | 久久激情片 | 欧美analxxxx | 久久久国产一区二区三区四区小说 | 日韩久久久久久久久久 | 天天色天天操综合 | 久久久性| 精品国产乱码一区二 | 九九综合久久 | 97在线免费观看视频 | 天天干夜夜爽 | 激情综合电影网 | 免费在线观看一区二区三区 | 免费成人短视频 | 久久精品超碰 | 色婷婷97 | 黄色特一级 | 日韩黄色一区 | 国产91区 | 久久亚洲欧美日韩精品专区 | 久久在线播放 | 欧美日韩国产二区 | 久久精久久精 | 激情欧美一区二区三区 | 黄色a一级视频 | 99久久er热在这里只有精品15 | 99精品热视频只有精品10 | 精品国产自在精品国产精野外直播 | 精品九九九 | 国产高清在线观看av | 精品成人在线 | 久久er99热精品一区二区 | 一级片视频在线 | 亚洲乱码久久久 | 国产无区一区二区三麻豆 | 免费v片 | 日日夜夜噜噜噜 | 欧美性精品 | 国产又粗又猛又色又黄网站 | 在线观看中文字幕亚洲 | 亚洲综合一区二区精品导航 | 免费亚洲成人 | 久久久国产精品视频 | 国产福利不卡视频 | 亚洲乱码在线观看 | 久久a视频| 欧美日韩中文在线视频 | 爱色av.com | 免费成人结看片 | 69精品在线 | 精品伊人久久久 | av大片网站 | 日韩午夜av | 久久久色| 99av国产精品欲麻豆 | 九九免费观看视频 | 国产精品色婷婷视频 | 91久久影院| 免费高清在线观看成人 | 国产自制av | 97精品超碰一区二区三区 | 久久人人爽爽人人爽人人片av | 国产69熟| a级一a一级在线观看 | 久久久亚洲麻豆日韩精品一区三区 | 久青草电影 | 日韩成人精品一区二区三区 | 性色va | av网站播放 | 亚洲三级毛片 | 国产成人久久久77777 | 日操操| 精品视频在线看 | 在线观看黄色小视频 | 日韩手机在线观看 | 久久国产精品一区二区 | 麻豆久久精品 | 亚洲激情在线播放 | 2020天天干夜夜爽 | 中文不卡视频 | 欧美激情在线看 | 中文字幕免费中文 | 国产亚洲精品v | www黄色av | 国产免费作爱视频 | 国产精品国产三级国产不产一地 | 青青河边草免费直播 | 欧美污在线观看 | 中文字幕一区在线观看视频 | 国产综合香蕉五月婷在线 | 久久久久www | 中文字幕视频观看 | 天天干,天天射,天天操,天天摸 | 成人欧美一区二区三区在线观看 | 韩国一区二区三区视频 | 国产一区二区精品久久91 | 国产亚洲精品综合一区91 | 中文在线免费视频 | 日韩欧美综合视频 | a在线观看免费视频 | av+在线播放在线播放 | 久久99精品久久久久久秒播蜜臀 | 97香蕉久久超级碰碰高清版 | 美女视频是黄的免费观看 | 久久久久久草 | 91亚洲精| 欧美性大战 | 亚州av一区 | 欧美整片sss | 中文字幕在线观看免费观看 | 视色网站| 国产一区电影在线观看 | 99久久久久久久久 | 成人av在线亚洲 | 丁香六月婷婷激情 | 一区二区三区电影 | 久久精品视频观看 | 欧美精品xxx| 免费看的黄网站 | 天天操天天综合网 | 麻豆一精品传二传媒短视频 | 久久久久久久福利 | 免费看一级一片 | 在线 视频 一区二区 | 狠狠色丁香婷婷综合视频 | 久久美女免费视频 | 精品一区二区三区香蕉蜜桃 | 超碰国产在线 | 国产精品av电影 | 超碰免费久久 | 欧美日韩国产网站 | 人人揉人人揉人人揉人人揉97 | 成人久久久久久久久 | 欧美作爱视频 | 黄色电影在线免费观看 | 亚洲免费小视频 | 国产99久久 | 久草视频免费播放 | 国产高清日韩欧美 | 99免费在线 | 日韩高清成人 | 国产精品女人久久久久久 | 中文成人字幕 | 亚洲免费在线视频 | 麻豆传媒视频在线 | 最新日韩电影 | 中文字幕九九 | 久久精品成人欧美大片古装 | 久久精品国产一区二区电影 | 久综合网 | 欧美在线a视频 | av综合站| 久草视频在线资源站 | 人成在线免费视频 | 91免费黄视频 | 免费a视频 | 狠狠狠色丁香婷婷综合久久五月 | 97福利视频 | 亚洲高清视频一区二区三区 | 黄污污网站 | 一级黄色大片 | 国产大陆亚洲精品国产 | 日韩激情在线视频 | 国产最新在线视频 | 免费观看一区二区 | 国产剧情一区二区在线观看 | 午夜成人免费影院 | 国产69久久久 | 久久精品久久精品久久精品 | 九九热精品在线 | 免费在线一区二区 | 久久手机在线视频 | 91久草视频 | 欧美人交a欧美精品 | 色视频在线免费观看 | 久久综合久久伊人 | 中文字幕av在线免费 | 国产不卡免费视频 | 国产精品毛片一区二区 | 欧美午夜激情网 | 国产精品久久久久久久久久三级 | 天天综合网国产 | 97色综合 | 91大片网站 | 99精品国产视频 | 欧美日韩一级久久久久久免费看 | 国产精品综合久久久久 | 久久亚洲热 | 探花视频在线观看+在线播放 | 五月婷婷久久丁香 | 日韩久久片 | 国产一及片 | 夜夜躁狠狠躁日日躁 | 日韩视频免费 | 99久久婷婷国产综合精品 | www激情久久 | www毛片com| 久久影院一区 | 手机成人免费视频 | 亚洲永久国产精品 | 国产伦精品一区二区三区免费 | 手机av片 | 3d黄动漫免费看 | 国产视频黄 | 久久天天躁狠狠躁夜夜不卡公司 | 激情av网 | 亚洲欧美综合精品久久成人 | 国产小视频在线播放 | 日本超碰在线 | 91免费版成人 | 久久午夜剧场 | 一区二区精品在线观看 | 激情久久一区二区三区 | 欧美精品黑人性xxxx | 黄色国产高清 | 久久国产精品一区二区三区四区 | 人人dvd | av网址最新 | 一区二区三区在线电影 | 开心激情久久 | 国产精品av在线 | 五月婷婷影院 | 在线观看av小说 | 高清在线观看av | 国产精品久久一区二区无卡 | 九九激情视频 | 亚洲网久久 | 九九九在线观看视频 | 中文在线免费一区三区 | 久久精品日产第一区二区三区乱码 | 国产视频不卡一区 | 国产一区在线视频播放 | 国产黄色片久久 | 青青草国产成人99久久 | 久久五月婷婷丁香 | 五月婷婷伊人网 | 毛片二区 | 久久综合久久综合这里只有精品 | 91视频麻豆视频 | aa一级片 | 在线观av | 日韩三区在线观看 | 一区中文字幕 | 欧美日韩高清 | av电影在线免费观看 | 亚洲精品视频在线播放 | 99久久精品日本一区二区免费 | 五月婷婷天堂 | 综合色亚洲 | 久久99国产一区二区三区 | 国产在线精品一区二区不卡了 | 在线观看日本韩国电影 | 中文字幕在线观看av | 国产精品久久久区三区天天噜 | 久草免费在线观看 | 日本精油按摩3 | 色综合亚洲精品激情狠狠 | 亚洲天天干| 成年人在线观看 | 最新久久免费视频 | 久久影视一区二区 | 三三级黄色片之日韩 | 日韩专区在线观看 | 久久视频免费在线 | 黄色app网站在线观看 | 色婷婷激情五月 | 国产97在线播放 | 日本精品视频一区二区 | 五月婷婷六月丁香 | 国产理论片在线观看 | 亚洲在线视频免费 | 久久国精品 | 免费看片网页 | 欧美日韩一区二区三区视频 | 成年人在线观看免费视频 | 黄色片网站 | 在线观看视频黄色 | 午夜国产一区二区三区四区 | 免费 在线 中文 日本 | 国产精品黄网站在线观看 | 国内精品视频久久 | 亚洲精品国产精品久久99 | 亚洲 欧美 91 | 亚洲欧洲中文日韩久久av乱码 | 日本精品一 | 狠狠色噜噜狠狠狠合久 | 日日爽视频 | 91精品久久久久 | 国产精品一区二区麻豆 | 中国一级特黄毛片大片久久 | 亚洲理论片在线观看 | 最近中文字幕在线 | 亚洲国产日韩欧美 | 亚洲日本韩国一区二区 | 国产青春久久久国产毛片 | 激情亚洲综合在线 | 日本精品视频一区二区 | 99久热在线精品 | 国产一级特黄电影 | 激情综合网五月婷婷 | 国产福利网站 | 永久免费毛片 | 中文字幕在线观看的网站 | 中文字幕色综合网 | 婷婷av在线| 激情视频免费在线 | 99久久影视| 精品国产一区二区三区噜噜噜 | 亚洲电影网站 | 在线欧美日韩 | 免费看特级毛片 | 在线观看 亚洲 | 97超碰福利久久精品 | 久久午夜网 | 欧美日韩不卡在线观看 | 国产一性一爱一乱一交 | av福利在线免费观看 | 麻豆av一区二区三区在线观看 | 国产五十路毛片 | 又黄又爽的免费高潮视频 | 中文字幕 在线看 | a视频免费 | 精品久久在线 | 在线看黄网站 | 天天夜操 | 99久久99 | 久草精品免费 | 久久久久久久久久久成人 | 国产精品久久久久久久久岛 | www.人人草 | 久久69av| 国产亚洲一区二区三区 | 久久国产亚洲视频 | 亚洲精品黄色在线观看 | 亚洲精品白浆高清久久久久久 | 99在线视频播放 | 国产福利一区二区三区在线观看 | 成人在线免费观看视视频 | 综合激情网 | 91色吧 | 天天摸天天弄 | 不卡视频一区二区三区 | 久久综合狠狠综合久久狠狠色综合 | 天天操天天曰 | 91精品婷婷国产综合久久蝌蚪 | 91九色老 | 亚洲精区二区三区四区麻豆 | av在线不卡观看 | 色婷婷亚洲精品 | 精品自拍网 | 日韩电影中文字幕在线观看 | avhd高清在线谜片 | 国内精品久久久久久中文字幕 | 婷婷在线资源 | 在线激情网 | 婷婷播播网 | 亚洲欧洲精品在线 | 精品久久精品 | 国产成人福利在线观看 | 91成人精品一区在线播放 | 日日夜夜免费精品 | 亚洲视频精品在线 | 日韩av中文字幕在线 | 精品久久久久久久 | 国产在线精品福利 | 香蕉网在线观看 | 99久久99热这里只有精品 | 99精品亚洲 | 久久亚洲免费 | 欧美va电影 | 久久国产午夜精品理论片最新版本 | 精品国产一二区 | av在线播放网址 | 国产精品高潮呻吟久久av无 | 精品国产一区二区三区在线观看 | 欧美日韩免费观看一区=区三区 | 天天射天天 | www.黄色片网站 | 91精品久久久久久综合乱菊 | 精品亚洲一区二区 | 91最新视频在线观看 | 热久久国产 | caobi视频| 国产精品久久久久久久久岛 | 婷婷久久久 | 久草a在线| 久爱精品在线 | 久久理论电影网 | 色综合久久悠悠 | 久久综合色播五月 | 少妇av片 | 九色激情网 | 欧美另类网站 | 久久国产露脸精品国产 | 伊人天堂av | 亚洲精品白浆高清久久久久久 | 色97在线| 久草免费色站 | 国产精品久久久久久久久费观看 | 欧美性天天| 日本一区二区免费在线观看 | 亚洲天堂精品视频在线观看 | 久久99久国产精品黄毛片入口 | 奇米影视999 | 欧美日韩精品免费观看视频 | 在线播放国产一区二区三区 | 亚洲四虎在线 | 天天曰天天曰 | 午夜影视av | 久久女教师 | 国产91成人 | 91大神一区二区三区 | 在线高清一区 | 最新婷婷色 | 欧美国产大片 | 国产在线精品视频 | 中文字幕乱码一区二区 | 97色狠狠| 视频在线精品 | 深爱激情丁香 | 欧美巨乳网 | 97人人模人人爽人人少妇 | 久草免费手机视频 | 亚洲免费在线播放视频 | 制服丝袜成人在线 | 日本精品一区二区三区在线观看 | 米奇狠狠狠888 | 国产精品手机视频 | 成人国产精品久久久久久亚洲 | 久久婷婷国产 | 久久久久免费精品国产小说色大师 | 日韩二区精品 | 中文字幕在线精品 | 久久精品国产99国产 | 久久99精品国产91久久来源 | 91中文视频| 青青啪 | 亚洲最新av | 黄色毛片视频免费 | 99视频在线精品国自产拍免费观看 | 国产精品网址在线观看 | 久久99中文字幕 | 2023天天干 | 成年人免费电影在线观看 | 天天插综合 | 久久人人爽av | 日日操天天操狠狠操 | 国产成人久久av977小说 | 婷婷精品国产欧美精品亚洲人人爽 | 国产在线观看中文字幕 | 国产经典 欧美精品 | 黄色av网站在线观看 | 欧美日韩国产综合网 | 国产精品一区在线观看你懂的 | 麻豆视频免费在线观看 | 狠狠干狠狠久久 | 亚洲男男gaygayxxxgv | 亚洲成人午夜在线 | 亚洲精品美女视频 | 国产一在线精品一区在线观看 | 国产亚洲欧美日韩高清 | 人人狠狠综合久久亚洲 | 久久成人国产精品免费软件 | 日韩欧美一区二区在线播放 | 91在线免费播放 | 欧美日韩二区三区 | 欧美日韩18 | 免费av一级电影 | 国产精品亚洲视频 | 亚洲,国产成人av | 综合黄色网 | 日日干日日色 | 草在线 | 天天夜夜亚洲 | www久 | 99免费视频 | 99视频精品| 天天干天天操 | 在线观看免费福利 | 五月天欧美精品 | 国产一区二区三区高清播放 | 国产又粗又猛又色 | 麻豆精品传媒视频 | 久久精品a | 天天爽夜夜爽精品视频婷婷 | 麻豆免费视频网站 | 超碰人人在线观看 | 免费看一级黄色大全 | 日韩精品观看 | 天天操天天曰 | 一本一本久久a久久精品牛牛影视 | 日韩r级在线 | 国产日韩精品在线观看 | 精品久久电影 | 中文字幕中文字幕在线中文字幕三区 | 色婷婷在线观看视频 | 中文字幕 二区 | 91福利视频在线 | 天天做天天爱夜夜爽 | 国产福利在线免费 | 成人亚洲网| 亚洲国产高清在线观看视频 | 国产精品毛片久久蜜 | 久久精品91久久久久久再现 | 亚洲人av免费网站 | 成人精品在线 | 国内精品久久久久影院男同志 | 午夜神马福利 | 欧美另类重口 | 岛国一区在线 | 亚洲v精品| 香蕉网在线播放 | 免费高清无人区完整版 | 亚洲国产资源 | 岛国av在线免费 | 热久久精品在线 | 在线观看免费黄色 | 日韩av影视在线 | 亚洲在线视频播放 | 在线欧美中文字幕 | 日日夜夜骑 | 久久精品视频一 | 在线免费高清 | 国产 日韩 在线 亚洲 字幕 中文 | 天天夜夜狠狠操 | 国产精品美乳一区二区免费 | 91亚洲激情 | 91视视频在线直接观看在线看网页在线看 | www.伊人网.com| 激情伊人五月天久久综合 | 狠狠色丁香婷婷综合久小说久 | 一区二区三区在线电影 | 欧美日韩国产成人 | 一二三区视频在线 | 日韩在线观看一区二区三区 | 欧美精品一区二区在线播放 | 久久少妇免费视频 | 国产精品成人免费一区久久羞羞 | 国产字幕在线看 | 成人综合婷婷国产精品久久免费 | 国产乱对白刺激视频在线观看女王 | 2019国产精品 | 欧美一区二区三区在线播放 | 亚洲免费av片 | 午夜成人影视 | 欧美日韩视频在线观看一区二区 | 亚洲精品美女视频 | 六月激情婷婷 | 亚洲电影一区二区 | 久久精品欧美一区二区三区麻豆 | 西西444www大胆高清图片 | 808电影 | 久久黄网站 | 国产精品永久免费观看 | 99色国产| 日韩一级电影在线 | 久久看看 | www.成人sex| 亚洲欧美婷婷六月色综合 | 欧美a视频 | 麻豆91精品 | 日韩av电影中文字幕在线观看 | 伊人开心激情 | 99热九九这里只有精品10 | 射射射av | 午夜av色| 久久久久久久久久久电影 | 成人黄大片视频在线观看 | 91看片淫黄大片一级在线观看 | 天天操夜夜干 | 香蕉精品在线观看 | 婷婷视频导航 | www色com | 四虎免费av| 免费观看十分钟 | 色姑娘综合天天 | 高清日韩一区二区 | 麻豆91精品91久久久 | 国内三级在线观看 | 久久老司机精品视频 | 天天天天天干 | 国产成人三级 | 成人av电影在线 | 日韩精品一区二区三区视频播放 | 国产精品久久久久久模特 | 日日精品 | 成人97视频 | 久草在线官网 | 国产麻豆精品95视频 | 毛片网在线 | 成人a级网站 | 毛片一区二区 | 欧美日韩亚洲第一 | 成人在线一区二区 | av中文字幕剧情 | 91高清在线 | 亚洲高清91 | av在线精品 | 国产视频亚洲 | 国产高清不卡av | 亚洲男人天堂a | 久久96国产精品久久99漫画 | 色噜噜在线观看视频 | 日韩高清免费电影 | 久草a在线| 国产精品一区二区免费看 | 国产精品视频最多的网站 | 狠狠色狠狠色合久久伊人 | 不卡视频在线看 | 一区二区三区中文字幕在线 | 又黄又爽又无遮挡免费的网站 | 香蕉久久久久 | 亚洲污视频 | 亚洲黄色片在线 | 中文字幕精品一区二区精品 | 国产日韩精品一区二区三区 | 日韩黄色一级电影 | 免费a v在线| 日韩网站视频 | 欧美日韩有码 | 亚洲国产精品推荐 | 国产精品成人免费一区久久羞羞 | 99在线免费观看视频 | 一级α片| 亚洲综合欧美日韩狠狠色 | 久久久这里有精品 | 久久美女电影 | 中文字幕精品www乱入免费视频 | 视频成人永久免费视频 | 国产精品美乳一区二区免费 | 免费观看国产成人 | 精品五月天 | 成人影视免费 | 92中文资源在线 | 一区二区三区免费 | 国产久视频| 亚洲国内精品在线 | 91福利在线导航 | 国产日韩精品在线 | 久久美女免费视频 | 日韩网站在线 | 久操视频在线播放 | 中文字幕在线观看一区二区三区 | 免费视频 三区 | 成年人看片 | 久久久久久久久久久久久9999 | 免费在线观看av网站 | 久久亚洲二区 | 99欧美精品 | 国产视频精品久久 | 激情丁香月 | 天天舔天天搞 | 蜜臀av.com | 91高清在线 | 四虎成人精品在永久免费 | 深夜精品福利 | 97在线观看免费视频 | 天天操天操 | 在线精品一区二区 | 在线观看日韩专区 | 欧美另类性 | 久久久久久久久久久久亚洲 | www操操操 | 免费在线中文字幕 | 日本久久久久久 | 超碰成人免费电影 | 丁香资源影视免费观看 | 欧美久草网 | 黄色软件网站在线观看 | 国产在线国产 | 婷婷久久综合九色综合 | 婷婷丁香五 | 青青五月天 | 日韩av免费在线看 | www.亚洲视频| 久久99精品久久久久婷婷 | 国产网站av | 在线视频 亚洲 | 99久热在线精品 | 午夜久久久精品 | 一区二区不卡视频在线观看 | 丁香五月亚洲综合在线 | 成人av电影在线播放 | 91成人精品国产刺激国语对白 | 成人中心免费视频 | av中文在线影视 | 欧美日韩中文视频 | 久久伊人操 | 99视频在线观看免费 | 中文字幕在线第一页 | 欧美精品久久天天躁 | 久久久福利 | 久久久精品视频网站 | 高清不卡毛片 | 18久久久 | 在线国产精品视频 | 成人久久18免费网站 | 久久成电影| 在线观看av麻豆 | 五月天综合网站 | 欧美精品亚州精品 | 992tv在线成人免费观看 | 9999亚洲| 久操视频在线免费看 | 欧美日韩在线观看一区二区三区 | 久久久久蜜桃 | 人人射av | 婷婷激情综合五月天 | 日韩在线视频在线观看 | 欧美一级性生活片 | 欧美伦理一区 | 亚洲成人黄色网址 | 国内精品在线一区 | 国产精品一区二区三区久久 | 精品国产三级a∨在线欧美 免费一级片在线观看 | 五月天丁香综合 | 国产一区久久久 | 日韩在线视频线视频免费网站 | 4438全国亚洲精品在线观看视频 | 国产亚洲精品日韩在线tv黄 | 人人干人人艹 | 久99久在线视频 | 久久综合狠狠综合久久综合88 | 伊人宗合网 | 日韩中文在线字幕 | 亚洲精品视频二区 | 色先锋资源网 | 国产精品二区在线 | 九九九九热精品免费视频点播观看 | 久久99久久99精品免观看软件 | 欧美日韩亚洲精品在线 | 在线看黄色的网站 | 成人黄色一级视频 | 久久精品一二三区 | 免费观看国产精品 | 福利久久久 | 久久久福利视频 | 中文字幕国产视频 | 成人国产精品电影 | 狠狠亚洲| 色婷婷免费视频 | 深夜国产福利 | 97精品国产97久久久久久粉红 | 精品国产午夜 | 国产精品毛片一区视频播 | 九九久久婷婷 | 久久国产精品一二三区 | 久久亚洲人 | 久草在线最新视频 | 精品久久久久亚洲 | 欧美日韩高清在线一区 | 九色免费视频 | av国产在线观看 | 国产一区在线免费观看视频 | 伊人资源站| 天天干天天在线 | 免费久久久久久 | 91色九色| 91麻豆看国产在线紧急地址 | 欧美大片在线观看一区 | 2019中文字幕网站 | 992tv在线| 一区二区免费不卡在线 | 激情久久伊人 | 日韩视频在线观看视频 | 夜又临在线观看 | 久久国产精品偷 | 欧美在线1 | 色综合久久久 | 欧美日韩成人一区 | 久久影院午夜论 | 最新一区二区三区 | 黄色福利网站 | 免费看污的网站 | 人人干天天射 | 一级黄色片在线免费观看 | 国产精品一区二区三区在线看 | 国产黄a三级三级三级三级三级 | 在线观看麻豆av | 激情五月综合网 | 91九色视频导航 | 日韩在线免费视频 | 亚洲精品乱码久久久久久蜜桃欧美 | 国产精品高潮呻吟久久av无 | 色天天综合久久久久综合片 | 96国产精品视频 | 99精品国产一区二区三区不卡 | 日韩欧美综合视频 | 婷婷中文在线 | 福利视频| 国产精品v欧美精品 | 在线欧美a | 久草视频免费看 | 高清av在线免费观看 | 午夜三级在线 | 国产精品久久久毛片 | 国产精品99免费看 | 日韩欧美在线高清 | 欧美日韩精品在线 | 激情综合网五月激情 | 狠狠色香婷婷久久亚洲精品 | 国产麻豆精品久久一二三 | 色婷婷免费视频 | 国内精品视频在线 | 婷婷六月天天 | 波多野结衣在线观看一区 | 欧美成人在线免费观看 | 亚洲精品美女在线观看播放 | 91av在线免费播放 | 国产亚洲精品久久网站 | 国产精品成人自产拍在线观看 | 韩国av永久免费 | 娇妻呻吟一区二区三区 | 久久久黄视频 | 麻豆视频成人 | 亚洲天堂社区 | 一区中文字幕 | 免费久久久 | 日韩电影在线一区二区 | 欧美激情精品久久久久久免费 | 一级性生活片 | 97超碰福利久久精品 | 国产原创在线 | 亚洲三级国产 | 美女黄视频免费看 | 久热av| 99精品国产福利在线观看免费 | 国产一级免费电影 | 成人免费电影 | 久久dvd | 欧美久久久久久久久久久 | 日韩精品一区二区三区外面 | 狠狠色噜噜狠狠狠合久 | 天天躁天天操 | 欧美日韩一级在线 | 精品久久在线 | 天天色天天干天天色 | 久久在线精品视频 | av免费在线网 | 一级国产视频 | 91成熟丰满女人少妇 | 又黄又爽又湿又无遮挡的在线视频 | 中文字幕在线第一页 | 天天色婷婷 | 四虎影视成人精品 | 在线看片一区 | 精品一区二区三区久久久 | 成人精品电影 | 亚洲日韩精品欧美一区二区 | 欧美粗又大 | 国产91免费在线观看 | 亚洲精品在线观看的 | 国产成人av网 | 成人av动漫在线 | 超碰国产在线播放 | www色网站 | 91av在线精品| 久久激情五月激情 | 黄色国产精品 | 国产一区二区精品91 | 超碰在线人人爱 | 中文字幕在线看片 | 日韩在线高清免费视频 | 亚洲色综合 | 伊人激情网 | 国产99色| 婷婷四房综合激情五月 | 午夜视频在线瓜伦 | 激情欧美一区二区免费视频 | 欧洲亚洲激情 | 96精品高清视频在线观看软件特色 | 国产精品女教师 | 成人午夜黄色 | 人人盈棋牌 | 视频一区二区国产 | 国产一级电影免费观看 | 日韩免费中文字幕 | 久久婷婷五月综合色丁香 | 久久精品系列 | 中文字幕精品久久 | 日韩av免费观看网站 | 国产日韩视频在线观看 | 中文国产在线观看 | 亚洲精品美女免费 | 久久久久国产精品免费免费搜索 | 国产自产在线视频 | 97av精品| 国产精品久久99精品毛片三a | 久久亚洲免费视频 | 久久在视频 | 色成人亚洲网 | 成年人在线电影 | 日本中文字幕电影在线免费观看 | 日韩欧美视频在线观看免费 | 视频福利在线观看 | 日韩精品视频免费在线观看 | 午夜精品视频一区二区三区在线看 | 国产99久久九九精品免费 | 国产精品黄色影片导航在线观看 | 中文字幕精品一区久久久久 | 成年人视频免费在线 | 成人精品国产免费网站 | 最近中文字幕免费视频 | 四虎影院在线观看av | 99久久爱 | 在线影院av | 久久精品中文 | 又黄又爽的免费高潮视频 | 久久九九视频 | 久久久久久久久久久影院 | 干综合网 | 精品国产一区二区三区日日嗨 | 91精品国产网站 | www.色国产 | 国产在线观看 | 97激情影院 | 国产精品wwwwww | 国产精品久久久久影院日本 | 精品久久久久亚洲 | 人人爽人人看 | 日韩精品中文字幕在线不卡尤物 | 欧美精品久久人人躁人人爽 | 在线免费观看国产视频 | 在线精品视频免费播放 | av三级在线免费观看 | 69av视频在线观看 | 亚洲91av | 99久久99热这里只有精品 | 97超碰资源站| 91手机视频 | 99精品久久久久 | 在线中文字母电影观看 | 女人高潮一级片 | 日韩xxxbbb | 在线免费av播放 | 国产精品色婷婷视频 | 国产精品18久久久久vr手机版特色 | 波多野结衣精品 | 国产精品久久久久久久久久久久久久 | 国产色综合天天综合网 | 国产流白浆高潮在线观看 | 午夜电影久久 | 国产在线观看你懂得 | 精品久久网站 | 欧美日韩国产色综合一二三四 | 操高跟美女 | 超碰在线人| 精品久久网 | 麻豆视频大全 | 在线观看小视频 | 色a在线观看 | 国产黄色精品 | 亚洲一区二区天堂 | 成人影视免费 | 国产精品网站 | 在线观看免费视频你懂的 | 久久超级碰视频 | 免费观看十分钟 | 午夜视频免费播放 | 国产 日韩 在线 亚洲 字幕 中文 | 亚洲一级免费观看 | 永久免费在线 | 国产福利电影网址 | 成人黄色片免费看 | 成人av电影免费在线播放 | 国产婷婷精品 | 麻豆国产精品视频 | 夜夜高潮夜夜爽国产伦精品 | 欧洲精品视频一区二区 | .精品久久久麻豆国产精品 亚洲va欧美 | 亚洲精品乱码白浆高清久久久久久 | 精品中文字幕在线观看 | 久草免费在线 | 中文在线www | 国产中文字幕视频在线观看 | 欧美精品乱码久久久久久 | 亚洲首页| 国产精品电影一区二区 |