iOS GCD的基本使用

GCD在iOS中多線程開發(fā)中使用頻繁,使用方便簡(jiǎn)單立哑,可以滿足我們大部分需求。其使用方法如下:

1、基本認(rèn)識(shí)

GCD以任務(wù)隊(duì)列為基礎(chǔ)的多線程管理的方案启妹,使用者不直接調(diào)用線程驱闷,而是調(diào)用隊(duì)列扶檐,往隊(duì)列中添加執(zhí)行任務(wù)迹缀,決定任務(wù)的執(zhí)行方式。
GCD中的隊(duì)列分為串行隊(duì)列种远、并行隊(duì)列涩澡,執(zhí)行方式分為同步執(zhí)行、異步執(zhí)行坠敷。
串行隊(duì)列:每次只能執(zhí)行一個(gè)任務(wù)妙同,等待任務(wù)執(zhí)行完畢才能執(zhí)行第下一個(gè)任務(wù)。
并行隊(duì)列:每次可以執(zhí)行多個(gè)任務(wù)膝迎,后面的任務(wù)不依賴前面的任務(wù)執(zhí)行情況粥帚。
同步執(zhí)行:執(zhí)行任務(wù)時(shí),需要當(dāng)前代碼執(zhí)行完畢才能執(zhí)行后面的代碼限次,會(huì)阻塞當(dāng)前線程
異步執(zhí)行:執(zhí)行任務(wù)時(shí)芒涡,當(dāng)前代碼不會(huì)影響后續(xù)任務(wù)外的代碼的執(zhí)行,不會(huì)阻塞當(dāng)前線程
創(chuàng)建方式如下:

    self.serial_queue = dispatch_queue_create("串行隊(duì)列", DISPATCH_QUEUE_SERIAL);
    
    self.concurrent_queue = dispatch_queue_create("并行隊(duì)列", DISPATCH_QUEUE_CONCURRENT);

其中隊(duì)列名稱可自行定義。
隊(duì)列和執(zhí)行方式有四種組合

a拖陆、串行隊(duì)列,同步執(zhí)行

任務(wù)按順序在當(dāng)前線程執(zhí)行懊亡,并且會(huì)阻塞當(dāng)前線程依啰,代碼如下:

- (void)sync_serial_queue {
    for (int i = 0; i < 20; i++) {
        dispatch_sync(self.serial_queue, ^{
            NSLog(@"%d===%@",i,[NSThread currentThread]);
        });
    }
}
b、串行隊(duì)列店枣,異步執(zhí)行

任務(wù)按順序執(zhí)行速警,不一定在當(dāng)前線程,不會(huì)阻塞當(dāng)前線程鸯两,代碼如下:

- (void)async_serial_queue {
    for (int i = 0; i < 20; i++) {
        dispatch_async(self.serial_queue, ^{
            NSLog(@"%d===%@",i,[NSThread currentThread]);
        });
    }
}
c闷旧、并行隊(duì)列,同步執(zhí)行

任務(wù)按順序執(zhí)行钧唐,不在當(dāng)前線程忙灼,會(huì)阻塞當(dāng)前線程,代碼如下:

- (void)sync_concurrent_queue {
    for (int i = 0; i < 20; i++) {
        dispatch_sync(self.concurrent_queue, ^{
            NSLog(@"%d===%@",i,[NSThread currentThread]);
        });
    }
}
d钝侠、并行隊(duì)列该园,異步執(zhí)行

任務(wù)不按順序執(zhí)行,不在當(dāng)前線程帅韧,不會(huì)阻塞當(dāng)前線程里初,代碼如下:

- (void)async_concurrent_queue {
    for (int i = 0; i < 20; i++) {
        dispatch_async(self.concurrent_queue, ^{
            NSLog(@"%d===%@",i,[NSThread currentThread]);
        });
    }
}

iOS的系統(tǒng)框架提供了兩個(gè)系統(tǒng)級(jí)隊(duì)列,分別為主隊(duì)列和全局隊(duì)列忽舟,特性如下:
主隊(duì)列:串行隊(duì)列双妨,任務(wù)只在主線程執(zhí)行
全局隊(duì)列:并行隊(duì)列,可設(shè)置優(yōu)先級(jí)
隊(duì)列獲取代碼如下:

    //主隊(duì)列
    dispatch_queue_t main_queue = dispatch_get_main_queue();
    
    //全局隊(duì)列
    dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);

在一般的需求下可以使用全局隊(duì)列叮阅,在代碼需要在主線程環(huán)境時(shí)使用主隊(duì)列刁品。

2、dispatch barrier的使用

當(dāng)我們存在以下情況帘饶,任務(wù)4依賴于任務(wù)1哑诊、任務(wù)2及刻、任務(wù)3的執(zhí)行,任務(wù)5缴饭、任務(wù)6、任務(wù)7依賴于任務(wù)4的執(zhí)行颗搂,其中任務(wù)1担猛、任務(wù)2、任務(wù)3可以分別獨(dú)立執(zhí)行傅联,任務(wù)5、任務(wù)6蒸走、任務(wù)7頁可以分別獨(dú)立同時(shí)執(zhí)行仇奶,如下圖:


圖片.png

則可以使用dispatch_barrier,函數(shù)有dispatch_barrier_sync和dispatch_barrier_async比驻,區(qū)別在于dispatch_barrier_sync會(huì)阻塞后續(xù)代碼的運(yùn)行别惦,dispatch_barrier_async不會(huì)阻塞后續(xù)代碼的運(yùn)行。兩個(gè)函數(shù)代碼如下:

/**
 異步并行隊(duì)列掸掸,barrier同步,使barrier前面的任務(wù)全部執(zhí)行完畢点晴,才會(huì)執(zhí)行barrier后面添加到queue的任務(wù),barrier_sync會(huì)影響后阻塞后續(xù)代碼的執(zhí)行
 */
- (void)concurrent_queue_barier_sync {
    NSLog(@"start");
    for (int i = 0; i < 10; i++) {
        dispatch_async(self.concurrent_queue, ^{
            NSLog(@"%d===%@",i,[NSThread currentThread]);
        });
        if (i == 5) {
            NSLog(@"barrier sync");
            dispatch_barrier_sync(self.concurrent_queue, ^{
                NSLog(@"dispatch_barrier_sync===%d===%@",i,[NSThread currentThread]);
            });
        }
    }
    NSLog(@"end");
}

/**
 異步并行隊(duì)列悯周,barrier同步,使barrier前面的任務(wù)全部執(zhí)行完畢屠橄,才會(huì)執(zhí)行barrier后面添加到queue的任務(wù)闰挡,barrier_async不會(huì)影響后續(xù)代碼的執(zhí)行
 */
- (void)concurrent_queue_barier_async {
    NSLog(@"start");
    for (int i = 0; i < 10; i++) {
        dispatch_async(self.concurrent_queue, ^{
            NSLog(@"%d===%@",i,[NSThread currentThread]);
        });
        if (i == 5) {
            NSLog(@"barrier async");
            dispatch_barrier_async(self.concurrent_queue, ^{
                NSLog(@"dispatch_barrier_async===%d===%@",i,[NSThread currentThread]);
            });
        }
    }
    NSLog(@"end");
}

3长酗、延遲執(zhí)行

當(dāng)我們需要延遲執(zhí)行某個(gè)任務(wù),一般情況下我們可以使用dispatch_after函數(shù)夺脾,代碼如下:

- (void)dispatch_after {
    NSLog(@"%@",[NSDate date]);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"after == %@",[NSDate date]);
    });
}

swift版本

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2, execute: DispatchWorkItem(block: {

}))

4咧叭、單次執(zhí)行

當(dāng)我們需要?jiǎng)?chuàng)建單例或者只需要代碼在程序運(yùn)行過程中只執(zhí)行一次時(shí),我們可以使用dispatch_once函數(shù)吉挣,代碼如下:

- (void)dispatch_once {
    for (int i = 0; i < 10; i++) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"%d",i);
        });
    }
}

5、多線程快速迭代

當(dāng)我們的數(shù)組需要在多線程情況下遍歷時(shí)可以使用dispatch_apply终吼,使用較少氯哮,代碼如下:

- (void)dispatch_apply {
    dispatch_apply(10, self.concurrent_queue, ^(size_t index) {
        NSLog(@"dispatch_apply==%zud===%@",index,[NSThread currentThread]);
    });
}

6、多任務(wù)同步

當(dāng)我們需要實(shí)現(xiàn)下面的任務(wù)時(shí)蛙粘,除了第二項(xiàng)的dispatch_barrier,我們還可以使用dispatch_group出牧。任務(wù)4依賴于任務(wù)1歇盼、任務(wù)2、任務(wù)3的執(zhí)行伯复,如下圖:


圖片.png

主要操作步驟:1啸如、創(chuàng)建dispatch_group氮惯;2、把任務(wù)加入到group帘不,等待任務(wù)執(zhí)行完畢杨箭;3、任務(wù)執(zhí)行完畢捣郊,調(diào)用dispatch_group_notify里面的任務(wù)擒悬。
第2步把任務(wù)加入到group中的操作有兩種方法,一種是直接使用dispatch_group_async侈净,另外一種是使用dispatch_group_enter、dispatch_group_leave兩個(gè)函數(shù)元扔,dispatch_group_enter表示進(jìn)入group旋膳,dispatch_group_leave表示一個(gè)任務(wù)執(zhí)行完畢,離開group擅羞。
我們也可以使用同步函數(shù)dispatch_group_wait來等待任務(wù)1义图、任務(wù)2、任務(wù)3執(zhí)行完畢娃承,然后執(zhí)行dispatch_group_wait后面的代碼怕篷。
代碼如下:

- (void)dispatch_group_1 {
    dispatch_group_t group_t = dispatch_group_create();
    
    for (int i = 0; i < 10; i++) {
        dispatch_group_async(group_t, self.concurrent_queue, ^{
            NSLog(@"dispatch_group_1===%d===%@",i,[NSThread currentThread]);
        });
    }
    
    dispatch_group_notify(group_t, dispatch_get_main_queue(), ^{
        NSLog(@"轉(zhuǎn)主線程");
    });
}

- (void)dispatch_group_2 {
    dispatch_group_t groutp_t = dispatch_group_create();
    
    for (int i = 0; i < 10; i++) {
        dispatch_group_enter(groutp_t);
        dispatch_async(self.concurrent_queue, ^{
            NSLog(@"dispatch_group_2===%d===%@",i,[NSThread currentThread]);
            dispatch_group_leave(groutp_t);
        });
    }
    dispatch_group_notify(groutp_t, dispatch_get_main_queue(), ^{
        NSLog(@"轉(zhuǎn)主線程");
    });
}

- (void)dispatch_group_3 {
    dispatch_group_t groutp_t = dispatch_group_create();
    
    for (int i = 0; i < 10; i++) {
        dispatch_group_enter(groutp_t);
        dispatch_async(self.concurrent_queue, ^{
            NSLog(@"dispatch_group_3===%d===%@",i,[NSThread currentThread]);
            dispatch_group_leave(groutp_t);
        });
    }
    dispatch_group_wait(groutp_t, DISPATCH_TIME_FOREVER);
    NSLog(@"group end");
}

7廊谓、信號(hào)量的使用

GCD提供了信號(hào)量函數(shù),提供我們進(jìn)行線程安全操作舔示,當(dāng)信號(hào)量為0時(shí)等待操作电抚,信號(hào)量大于0是,繼續(xù)執(zhí)行操作俺祠,借帘,主要流程如下:
1肺然、創(chuàng)建信號(hào)量對(duì)象,確定信號(hào)初始值
2际起、開始執(zhí)行操作前檢查是否需要等待吐葱,執(zhí)行函數(shù)dispatch_semaphore_wait弟跑,如果當(dāng)前的信號(hào)量為0則阻塞防症,等待信號(hào)量大于0時(shí)繼續(xù)執(zhí)行,執(zhí)行函數(shù)完畢會(huì)使信號(hào)量減一饲嗽。當(dāng)一個(gè)任務(wù)執(zhí)行完畢時(shí)我們需要調(diào)用dispatch_semaphore_signal函數(shù)奈嘿,告訴系統(tǒng)一個(gè)任務(wù)執(zhí)行完畢,調(diào)用后會(huì)使信號(hào)量加1。dispatch_semaphore_wait和dispatch_semaphore_signal函數(shù)應(yīng)當(dāng)成對(duì)存在伯诬,有特別需求也可以不成對(duì)存在巫财。當(dāng)初始信號(hào)量為1時(shí),我們可以用作線程安全鎖進(jìn)行使用赫舒,代碼如下:

- (void)dispatch_semaphore {
    dispatch_semaphore_t semaphore_t = dispatch_semaphore_create(3);
    for (int i = 0; i < 10; i++) {
        dispatch_semaphore_wait(semaphore_t, DISPATCH_TIME_FOREVER);
        dispatch_async(self.concurrent_queue, ^{
            int t = rand() % 5;
            sleep(t);
            NSLog(@"dispatch_semaphore===%d===%@===%d",i,[NSThread currentThread],t);
            dispatch_semaphore_signal(semaphore_t);
        });
    }
    NSLog(@"end");
}

8闽瓢、GCD的source事件

GCD可以監(jiān)聽事件源對(duì)象扣讼,創(chuàng)建事件源對(duì)象方法如下:

dispatch_source_t
dispatch_source_create(dispatch_source_type_t type,
    uintptr_t handle,
    uintptr_t mask,
    dispatch_queue_t _Nullable queue);

事件源類型有很多種,如下所示:

    // 自定義事件,觸發(fā)事件后的數(shù)據(jù)會(huì)被疊加運(yùn)算
    #define DISPATCH_SOURCE_TYPE_DATA_ADD
    // 自定義事件,觸發(fā)時(shí)間后的數(shù)據(jù)會(huì)被按位或運(yùn)算
    #define DISPATCH_SOURCE_TYPE_DATA_OR
    // 自定義事件,觸發(fā)時(shí)間后的數(shù)據(jù)會(huì)被替換
    #define DISPATCH_SOURCE_TYPE_DATA_REPLACE
    // 內(nèi)核端口發(fā)送數(shù)據(jù)事件
    #define DISPATCH_SOURCE_TYPE_MACH_SEND
    // 內(nèi)核端口接收數(shù)據(jù)事件
    #define DISPATCH_SOURCE_TYPE_MACH_RECV
    // 內(nèi)存壓力事件
    #define DISPATCH_SOURCE_TYPE_MEMORYPRESSURE
    // 進(jìn)程相關(guān)事件
    #define DISPATCH_SOURCE_TYPE_PROC
    // 讀文件相關(guān)事件
    #define DISPATCH_SOURCE_TYPE_READ
    // 寫文件相關(guān)事件
    #define DISPATCH_SOURCE_TYPE_WRITE
    // 信號(hào)相關(guān)事件
    #define DISPATCH_SOURCE_TYPE_SIGNAL
    // 定時(shí)器事件
    #define DISPATCH_SOURCE_TYPE_TIMER
    // 文件屬性修改事件
    #define DISPATCH_SOURCE_TYPE_VNODE

創(chuàng)建事件源之后荔燎,我們需要設(shè)置回調(diào)函數(shù)销钝,如下:

void
dispatch_source_set_event_handler(dispatch_source_t source,
    dispatch_block_t _Nullable handler);

注意創(chuàng)建的dispatch_source_t對(duì)象需要我們進(jìn)行強(qiáng)引用蒸健,持有對(duì)象婉商。
最后我們需要激活對(duì)象征讲,方法如下诗箍,傳入dispatch_source_t作為參數(shù)。

void
dispatch_activate(dispatch_object_t object);

定時(shí)器是我們常使用的一種任務(wù)邏輯滤祖,當(dāng)我們需要較高精度的定時(shí)器時(shí)我們可以使用GCD source的定時(shí)器執(zhí)行任務(wù)匠童,示例代碼如下:

- (void)dispatch_source_timer {
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.serial_queue);
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"dispatch_source_timer %@",[NSThread currentThread]);
    });
    dispatch_activate(timer);
    self.timer = timer;
}

demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汤求,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子竖独,更是在濱河造成了極大的恐慌挤牛,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竞膳,死亡現(xiàn)場(chǎng)離奇詭異诫硕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)长窄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門挠日,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翰舌,“玉大人,你說我怎么就攤上這事懂算。” “怎么了喜德?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵垮媒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我萌衬,道長(zhǎng)它抱,這世上最難降的妖魔是什么观蓄? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮歌径,結(jié)果婚禮上撮珠,老公的妹妹穿的比我還像新娘金矛。我一直安慰自己,他們只是感情好娶耍,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布榕酒。 她就那樣靜靜地躺著故俐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辑舷。 梳的紋絲不亂的頭發(fā)上槽片,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音碌廓,去河邊找鬼。 笑死慨蛙,一個(gè)胖子當(dāng)著我的面吹牛波材,可吹牛的內(nèi)容都是我干的廷区。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼埠帕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼玖绿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呐籽,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蚀瘸,失蹤者是張志新(化名)和其女友劉穎贮勃,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寂嘉,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泉孩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年寓搬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曼尊。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瞒御,靈堂內(nèi)的尸體忽然破棺而出神郊,到底是詐尸還是另有隱情,我是刑警寧澤蜻懦,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布夕晓,位于F島的核電站蒸辆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏躬贡。R本人自食惡果不足惜拂玻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魄懂。 院中可真熱鬧熬甚,春花似錦肋坚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至葵第,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缀台,已是汗流浹背哮奇。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工鼎俘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贸伐。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓棍丐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親巾钉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秘案,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

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