GCD基本用法大全

GCD是Apple開發(fā)的一個(gè)多核編程的解決方案唆垃,他可以用于多核的并行運(yùn)算接校,并自動(dòng)利用更多的CPU內(nèi)核筹吐,同時(shí)GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程糖耸、調(diào)度任務(wù)、銷毀線程)丘薛,而且我們使用GCD時(shí)嘉竟,只需要調(diào)用GCD的API來填入需要執(zhí)行的任務(wù),而不需要編寫任何線程管理的代碼

一洋侨、任務(wù)和隊(duì)列

任務(wù):

執(zhí)行的操作舍扰,任務(wù)分為同步執(zhí)行和異步執(zhí)行兩種。

  • 同步(sync):同步添加任務(wù)到指定的隊(duì)列中希坚,在添加的任務(wù)執(zhí)行結(jié)束之前边苹,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行裁僧,即會(huì)阻塞線程个束。只能在當(dāng)前線程中執(zhí)行任務(wù)(是當(dāng)前線程慕购,不一定是主線程),不具備開啟新線程的能力茬底。API為dispatch_sync這類函數(shù)

  • 異步(async):線程會(huì)立即返回沪悲,無需等待就會(huì)繼續(xù)執(zhí)行下面的任務(wù),不阻塞當(dāng)前線程阱表〉钊纾可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力(并不一定開啟新線程)捶枢。如果不是添加到主隊(duì)列上握截,異步會(huì)在子線程中執(zhí)行任務(wù)。API為dispatch_async這類函數(shù)

隊(duì)列

隊(duì)列:存放任務(wù)的隊(duì)列烂叔,隊(duì)列是一種特殊的線性表谨胞,遵從FIFO(First in first out,先進(jìn)先出)原則,分為兩種:串行隊(duì)列和并行隊(duì)列

  • 并行隊(duì)列(concurrent):多個(gè)任務(wù)同時(shí)執(zhí)行(每個(gè)任務(wù)在不同的線程中執(zhí)行),API為dispatch_async這類函數(shù)

  • 串行隊(duì)列(serial):任務(wù)按照順序執(zhí)行(各個(gè)任務(wù)可能在同一個(gè)線程蒜鸡,也可能在不同的線程)胯努,API為dispatch_sync這類函數(shù)

二、GCD四種基礎(chǔ)操作

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

- (void)dispatchSyncSerial{
    //這個(gè)函數(shù)創(chuàng)建隊(duì)列逢防,第一個(gè)參數(shù)為隊(duì)列名叶沛,第二個(gè)參數(shù)標(biāo)記創(chuàng)建的隊(duì)列是串行隊(duì)列還是并行隊(duì)列,使用兩個(gè)宏定義來創(chuàng)建忘朝,DISPATCH_QUEUE_SERIAL為串行隊(duì)列灰署,DISPATCH_QUEUE_CONCURRENT為并行隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("串行隊(duì)列名", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        sleep(1);
        NSLog(@"在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        sleep(2);
        NSLog(@"在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
}

/* 結(jié)果

2017-08-22 18:42:55.397 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:{number = 1, name = main}

2017-08-22 18:42:55.398 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:{number = 1, name = main}

2017-08-22 18:42:57.399 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:{number = 1, name = main}

2017-08-22 18:42:57.400 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:{number = 1, name = main}

*/

結(jié)論:串行隊(duì)列+同步執(zhí)行,任務(wù)按照添加的順序局嘁,依次執(zhí)行,并且都在同一個(gè)線程中執(zhí)行

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

- (void)dispatchAsyncSerial{
    dispatch_queue_t queue = dispatch_queue_create("串行隊(duì)列名", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
}
/* 結(jié)果 
2017-08-22 18:43:45.006 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:{number = 3, name = (null)}

2017-08-22 18:43:45.006 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:{number = 3, name = (null)}

2017-08-22 18:43:47.011 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:{number = 3, name = (null)}

2017-08-22 18:43:47.011 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:{number = 3, name = (null)}

*/

結(jié)論:串行隊(duì)列 + 異步執(zhí)行溉箕,雖然各個(gè)任務(wù)都在在子線程中執(zhí)行,但仍然是按照添加入隊(duì)列的順序悦昵,依次執(zhí)行

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

- (void)dispatchSyncConcurrent{
    dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        sleep(1);
        NSLog(@"在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        sleep(2);
        NSLog(@"在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
}
/*
 結(jié)果
 2017-08-22 18:48:30.946 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
 2017-08-22 18:48:30.947 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
 2017-08-22 18:48:32.948 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
 2017-08-22 18:48:32.949 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
 */

結(jié)論:并行隊(duì)列 + 同步執(zhí)行肴茄,雖然隊(duì)里是并行隊(duì)列,但是由于是同步執(zhí)行但指,所有任務(wù)都在主線程中執(zhí)行寡痰,他們?nèi)匀皇前凑仗砑尤腙?duì)列的順序,依次執(zhí)行

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

- (void)dispatchAsyncConcurrent{
    dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
}
/*
 結(jié)果
 2017-08-22 18:51:02.246 GCD簡(jiǎn)介與Demo[4574:274917] 在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:<NSThread: 0x608000272840>{number = 3, name = (null)}
 2017-08-22 18:51:02.246 GCD簡(jiǎn)介與Demo[4574:274937] 在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:<NSThread: 0x600000267980>{number = 4, name = (null)}
 2017-08-22 18:51:03.249 GCD簡(jiǎn)介與Demo[4574:274918] 在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:<NSThread: 0x600000265ac0>{number = 5, name = (null)}
 2017-08-22 18:51:04.250 GCD簡(jiǎn)介與Demo[4574:274920] 在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:<NSThread: 0x608000266240>{number = 6, name = (null)}
 */

結(jié)論:并行隊(duì)列 + 異步執(zhí)行棋凳,各個(gè)任務(wù)都在子線程中執(zhí)行拦坠,并且同時(shí)開始執(zhí)行

5.GCD中的特殊隊(duì)列

  1. 主隊(duì)列:GCD自帶的一個(gè)特殊的串行隊(duì)列,使用dispatch_get_main_queue()獲取

  2. 全局隊(duì)列:GCD自帶的一個(gè)特殊的并行隊(duì)列剩岳,使用dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)獲取

注意:DISPATCH_QUEUE_PRIORITY_DEFAULT是一個(gè)宏贪婉,它標(biāo)記隊(duì)列的優(yōu)先級(jí),有以下四種:

  • DISPATCH_QUEUE_PRIORITY_HIGH 高優(yōu)先級(jí)

  • DISPATCH_QUEUE_PRIORITY_DEFAULT 默認(rèn)優(yōu)先級(jí)

  • DISPATCH_QUEUE_PRIORITY_LOW 低優(yōu)先級(jí)

  • DISPATCH_QUEUE_PRIORITY_BACKGROUND 后臺(tái)優(yōu)先級(jí)

三、線程間的通信以及單次執(zhí)行方法

1.線程間的通信

一般用于在將網(wǎng)絡(luò)請(qǐng)求卢肃、下載疲迂、上傳等耗時(shí)操作放入子線程執(zhí)行完畢后,回到主線程更新UI

注意:UI操作必須在主線程進(jìn)行操作

- (void)gcdSendMessageFromAnotherThread{
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"開始執(zhí)行耗時(shí)操作,所處線程:%@",[NSThread currentThread]);
        sleep(3);
        NSLog(@"耗時(shí)操作執(zhí)行完畢莫湘,開始進(jìn)入主線程");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"進(jìn)入主線程尤蒿,開始執(zhí)行更新UI操作,所處線程:%@",[NSThread currentThread]);
        });
        
    });
}
/*
 結(jié)果
 2017-08-22 19:07:09.099 GCD簡(jiǎn)介與Demo[4698:290444] 開始執(zhí)行耗時(shí)操作,所處線程:<NSThread: 0x600000263600>{number = 3, name = (null)}
 2017-08-22 19:07:12.103 GCD簡(jiǎn)介與Demo[4698:290444] 耗時(shí)操作執(zhí)行完畢,開始進(jìn)入主線程
 2017-08-22 19:07:12.103 GCD簡(jiǎn)介與Demo[4698:290380] 進(jìn)入主線程幅垮,開始執(zhí)行更新UI操作,所處線程:<NSThread: 0x600000078f40>{number = 1, name = main}
 */

2.單次執(zhí)行 dispatch_once 一般用于創(chuàng)建單例

- (void)dispatchOnce{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"這里是只執(zhí)行一次的代碼,所處線程:%@",[NSThread currentThread]);
    });
}
- (void)testDispatchOnce{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第一次,所處線程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第二次,所處線程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第三次,所處線程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第四次,所處線程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
}
/* 
 結(jié)果
 2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295603] 第三次,所處線程:<NSThread: 0x6000002604c0>{number = 5, name = (null)}
 2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295617] 第四次,所處線程:<NSThread: 0x600000260600>{number = 6, name = (null)}
 2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295600] 第二次,所處線程:<NSThread: 0x608000263e40>{number = 4, name = (null)}
 2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295601] 第一次,所處線程:<NSThread: 0x608000263f00>{number = 3, name = (null)}
 2017-08-22 19:12:15.814 GCD簡(jiǎn)介與Demo[4729:295603] 這里是只執(zhí)行一次的代碼,所處線程:<NSThread: 0x6000002604c0>{number = 5, name = (null)}
 */

結(jié)論:dispatch_once封裝的代碼只在第一次執(zhí)行了一次

四腰池、GCD的其它處理函數(shù)

1.柵欄方法dispatch_barrier

這個(gè)函數(shù)相當(dāng)于一個(gè)停頓,在這個(gè)函數(shù)之前加入隊(duì)列的任務(wù)先執(zhí)行忙芒,執(zhí)行完畢后執(zhí)行柵欄函數(shù)中的任務(wù)示弓,然后再執(zhí)行柵欄函數(shù)之后的任務(wù)

注意:dispatch_barrier_async和dispatch_barrier_sync的區(qū)別:僅僅是執(zhí)行的線程不同,dispatch_barrier_sync會(huì)同步執(zhí)行(在主線程中執(zhí)行)呵萨,dispatch_barrier_async在子線程中執(zhí)行

- (void)dispatchBarrier{
    dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_barrier_sync(queue, ^{
        sleep(1);
        NSLog(@"執(zhí)行barrier同步任務(wù),線程:%@",[NSThread currentThread]);
        sleep(1);
    });
    dispatch_barrier_async(queue, ^{
        sleep(1);
        NSLog(@"執(zhí)行barrier異步任務(wù),線程:%@",[NSThread currentThread]);
        sleep(1);
    });
    dispatch_async(queue, ^{
        NSLog(@"第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
    });
}
/*
 結(jié)果:
 2017-08-22 19:35:16.874 GCD簡(jiǎn)介與Demo[4949:314086] 第一個(gè)任務(wù),線程:<NSThread: 0x60800007b5c0>{number = 3, name = (null)}
 2017-08-22 19:35:16.874 GCD簡(jiǎn)介與Demo[4949:314083] 第二個(gè)任務(wù),線程:<NSThread: 0x60800007b680>{number = 4, name = (null)}
 2017-08-22 19:35:17.876 GCD簡(jiǎn)介與Demo[4949:313797] 執(zhí)行barrier同步任務(wù),線程:<NSThread: 0x6080000708c0>{number = 1, name = main}
 2017-08-22 19:35:19.882 GCD簡(jiǎn)介與Demo[4949:314083] 執(zhí)行barrier異步任務(wù),線程:<NSThread: 0x60800007b680>{number = 4, name = (null)}
 2017-08-22 19:35:20.887 GCD簡(jiǎn)介與Demo[4949:314083] 第三個(gè)任務(wù),線程:<NSThread: 0x60800007b680>{number = 4, name = (null)}
 2017-08-22 19:35:20.887 GCD簡(jiǎn)介與Demo[4949:314084] 第四個(gè)任務(wù),線程:<NSThread: 0x60800007b7c0>{number = 5, name = (null)}
 */

2.GCD延遲執(zhí)行的方法dispatch_after

- (void)dispatchAfter{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"執(zhí)行子線程任務(wù),線程:%@",[NSThread currentThread]);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"延時(shí)兩秒后執(zhí)行主線程任務(wù),線程:%@",[NSThread currentThread]);
        });
    });
    
}
/*
 結(jié)果
 2017-08-22 19:40:10.880 GCD簡(jiǎn)介與Demo[4982:319700] 執(zhí)行子線程任務(wù),線程:<NSThread: 0x60000006b8c0>{number = 3, name = (null)}
 2017-08-22 19:40:13.072 GCD簡(jiǎn)介與Demo[4982:319625] 延時(shí)兩秒后執(zhí)行主線程任務(wù),線程:<NSThread: 0x6080000674c0>{number = 1, name = main}
 */

3.GCD的快速迭代方法dispatch_apply

dispatch_apply類似for循環(huán)奏属,它是一個(gè)同步調(diào)用,任務(wù)將會(huì)執(zhí)行給定次數(shù)潮峦,如果是并行隊(duì)里中囱皿,那么會(huì)并發(fā)執(zhí)行任務(wù),如果是串行隊(duì)列中忱嘹,則會(huì)順序執(zhí)行其中的代碼

應(yīng)用場(chǎng)景:

  1. 防止創(chuàng)建過多線程導(dǎo)致線程爆炸

  2. 多個(gè)任務(wù)并行執(zhí)行嘱腥,比如字典數(shù)組的快速解析

- (void)dispatchApplyAsync{
    NSArray *array = @[@1,@2,@3,@4,@5];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       dispatch_apply(array.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
           NSLog(@"%@開始執(zhí)行 %zu times",[NSThread currentThread],index+1);
           sleep((unsigned int)(index+1));
       });
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主線程操作");
        });
    });
}
/*
 結(jié)果
 2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331292] <NSThread: 0x600000079940>{number = 5, name = (null)}開始執(zhí)行 3 times
 2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331304] <NSThread: 0x60800007f040>{number = 4, name = (null)}開始執(zhí)行 2 times
 2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331290] <NSThread: 0x600000079840>{number = 3, name = (null)}開始執(zhí)行 1 times
 2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331289] <NSThread: 0x600000079980>{number = 6, name = (null)}開始執(zhí)行 4 times
 2017-08-22 19:54:38.940 GCD簡(jiǎn)介與Demo[5088:331290] <NSThread: 0x600000079840>{number = 3, name = (null)}開始執(zhí)行 5 times
 2017-08-22 19:54:42.943 GCD簡(jiǎn)介與Demo[5088:331197] 回到主線程操作
 */

可以看出,dispatch_apply之后的操作將會(huì)在dispatch_apply全部執(zhí)行完畢后才開始執(zhí)行

- (void)dispatchApplySync{
    NSArray *array = @[@1,@2,@3,@4,@5];
    dispatch_queue_t queue = dispatch_queue_create("串行隊(duì)列名", DISPATCH_QUEUE_SERIAL);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_apply(array.count, queue, ^(size_t index) {
            NSLog(@"%@開始執(zhí)行 %zu times",[NSThread currentThread],index+1);
            sleep((unsigned int)(index+1));
        });
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主線程操作");
        });
    });
}
/*
 結(jié)果
 2017-08-22 19:58:03.952 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 1 times
 2017-08-22 19:58:04.954 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 2 times
 2017-08-22 19:58:06.958 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 3 times
 2017-08-22 19:58:09.963 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 4 times
 2017-08-22 19:58:13.969 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 5 times
 2017-08-22 19:58:18.972 GCD簡(jiǎn)介與Demo[5110:334292] 回到主線程操作
 */

可以看出拘悦,串行隊(duì)列中齿兔,所有的任務(wù)按照順序依次執(zhí)行,執(zhí)行完畢后础米,再執(zhí)行后面的代碼

4.GCD隊(duì)列組dispatch_group_t

應(yīng)用場(chǎng)景:多個(gè)任務(wù)并發(fā)執(zhí)行分苇,我們需要獲取到全部執(zhí)行完畢后的時(shí)間節(jié)點(diǎn)(使用dispatch_group_notify獲取)

- (void)dispatchGroup{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        NSLog(@"執(zhí)行完第一個(gè)任務(wù)");
    });
    dispatch_group_async(group, queue, ^{
        sleep(1);
        NSLog(@"執(zhí)行完第二個(gè)任務(wù)");
    });
    dispatch_group_async(group, queue, ^{
        sleep(2);
        NSLog(@"執(zhí)行完第三個(gè)任務(wù)");
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任務(wù)全部執(zhí)行完畢");
    });
}
/*
 2017-08-22 20:06:04.304 GCD簡(jiǎn)介與Demo[5155:341594] 執(zhí)行完第一個(gè)任務(wù)
 2017-08-22 20:06:05.306 GCD簡(jiǎn)介與Demo[5155:341596] 執(zhí)行完第二個(gè)任務(wù)
 2017-08-22 20:06:06.306 GCD簡(jiǎn)介與Demo[5155:341593] 執(zhí)行完第三個(gè)任務(wù)
 2017-08-22 20:06:06.306 GCD簡(jiǎn)介與Demo[5155:341593] 任務(wù)全部執(zhí)行完畢
 */

五、GCD使用信號(hào)量控制并發(fā)dispatch_semaphore

1.函數(shù)講解:

  • dispatch_semaphore_create

函數(shù)原型:dispatch_samaphore_t dispatch_semaphore_create(long value)

傳入的參數(shù)為long椭盏,輸出一個(gè)dispatch_semaphore_t類型且值為value的信號(hào)量组砚。 值得注意的是,這里的傳入的參數(shù)value必須大于或等于0掏颊,否則dispatch_semaphore_create會(huì)返回NULL糟红。

  • dispatch_semaphore_signal

函數(shù)原型:long dispatch_semaphore_signal(dispatch_semaphore_tdsema)

這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值加1

當(dāng)返回值為0時(shí)表示當(dāng)前并沒有線程等待其處理的信號(hào)量,其處理的信號(hào)量的值加1即可乌叶。當(dāng)返回值不為0時(shí)盆偿,表示其當(dāng)前有(一個(gè)或多個(gè))線程等待其處理的信號(hào)量,并且該函數(shù)喚醒了一個(gè)等待的線程(當(dāng)線程有優(yōu)先級(jí)時(shí)准浴,喚醒優(yōu)先級(jí)最高的線程事扭;否則隨機(jī)喚醒)。

  • dispatch_semaphore_wait

函數(shù)原型: long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)

這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值減1

如果desema的值為0乐横,那么這個(gè)函數(shù)就阻塞當(dāng)前線程等待timeout(注意timeout的類型為dispatch_time_t)

不能直接傳入整形或float型數(shù)求橄,如果等待的期間desema的值被dispatch_semaphore_signal函數(shù)加1了今野,且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號(hào)量,那么就繼續(xù)向下執(zhí)行并將信號(hào)量減1罐农。

如果等待期間沒有獲取到信號(hào)量或者信號(hào)量的值一直為0条霜,那么等到timeout時(shí),其所處線程自動(dòng)執(zhí)行其后語句涵亏。

dispatch_semaphore_wait的返回值也為long型宰睡。當(dāng)其返回0時(shí)表示在timeout之前,該函數(shù)所處的線程被成功喚醒气筋。當(dāng)其返回不為0時(shí)拆内,表示timeout發(fā)生。

2.timeout參數(shù)

  • DISPATCH_TIME_NOW 表示當(dāng)前(立刻超時(shí))

  • DISPATCH_TIME_FOREVER  表示遙遠(yuǎn)的未來(永不超時(shí))

具體可參照:關(guān)于dispatch_semaphore的使用

- (void)dispatchSemaphore{
    //創(chuàng)建信號(hào)宠默,設(shè)置最大并發(fā)數(shù)為2
    dispatch_semaphore_t sem = dispatch_semaphore_create(2);
    for (int i = 0; i < 10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            long a = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            NSLog(@"第%d個(gè)任務(wù)執(zhí)行,此時(shí)a為:%ld",i+1,a);
            sleep(1);
            long b = dispatch_semaphore_signal(sem);
            NSLog(@"b為:%ld",b);
        });
    }
}
/*
 結(jié)果
 2017-08-22 21:11:48.928 GCD簡(jiǎn)介與Demo[716:12343] 第2個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:48.928 GCD簡(jiǎn)介與Demo[716:12330] 第1個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:49.931 GCD簡(jiǎn)介與Demo[716:12329] 第4個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:49.931 GCD簡(jiǎn)介與Demo[716:12332] 第3個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:50.933 GCD簡(jiǎn)介與Demo[716:12345] 第5個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:50.933 GCD簡(jiǎn)介與Demo[716:12346] 第6個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:51.937 GCD簡(jiǎn)介與Demo[716:12347] 第7個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:51.937 GCD簡(jiǎn)介與Demo[716:12348] 第8個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:52.940 GCD簡(jiǎn)介與Demo[716:12350] 第10個(gè)任務(wù)執(zhí)行
 2017-08-22 21:11:52.940 GCD簡(jiǎn)介與Demo[716:12349] 第9個(gè)任務(wù)執(zhí)行
 */

可以看出麸恍,同時(shí)執(zhí)行任務(wù)的只有兩個(gè),這就控制了最大并發(fā)數(shù)

六光稼、GCD定時(shí)器 dispatch_source_set_timer

NSTimer是我們最為熟悉的定時(shí)器或南,但是NSTimer有很大的缺點(diǎn),并不準(zhǔn)確艾君。而GCD定時(shí)器采够,則是嚴(yán)格按照規(guī)定好的規(guī)格去做事,它更為準(zhǔn)確

NSTimer是在RunLoop的基礎(chǔ)上執(zhí)行的冰垄,而RunLoop是在GCD的基礎(chǔ)上實(shí)現(xiàn)的蹬癌,所以GCD可以算是更為高級(jí)

@interface ViewController ()
//這里不需要*號(hào),因?yàn)閐ispatch_source_t實(shí)現(xiàn)中已經(jīng)加進(jìn)了指針*號(hào)
@property (nonatomic,strong) dispatch_source_t timer;

@property (nonatomic,strong) UIView *animationView;

@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _animationView = [[UIView alloc]initWithFrame:CGRectMake(0, self.view.bounds.size.height/2.-50, 100, 100)];
    _animationView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_animationView];
    [self beginGCDTimer];
}
- (void)beginGCDTimer{
    //如果定時(shí)器已經(jīng)開啟虹茶,則將它關(guān)閉逝薪,并置空
    if (self.timer) {
        dispatch_source_cancel(self.timer);
        self.timer = nil;
    }
    //創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
    //創(chuàng)建定時(shí)器
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //設(shè)置開始時(shí)間 后面的參數(shù)意思為從現(xiàn)在開始后2秒開始執(zhí)行定時(shí)器回調(diào)
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC);
    //設(shè)置時(shí)間間隔 uint64_t為unsigned long long的別名,無符號(hào)長(zhǎng)長(zhǎng)整形
    uint64_t interval = 0.01 * NSEC_PER_SEC;
    //設(shè)置定時(shí)器 最后一個(gè)參數(shù)為偏差蝴罪,一般設(shè)置為0
    dispatch_source_set_timer(_timer, start, interval, 0);
    //設(shè)置回調(diào)
    dispatch_source_set_event_handler(_timer, ^{
        CGFloat x = self.animationView.frame.origin.x;
        CGFloat width = self.view.bounds.size.width;
        x += 1;
        if (x >= width) {
            x = 0;
        }
        CGRect frame = self.animationView.frame;
        frame.origin.x = x;
        dispatch_async(dispatch_get_main_queue(), ^{
            self.animationView.frame = frame;
        });
    });
    //定時(shí)器默認(rèn)是暫停的董济,需要手動(dòng)啟動(dòng)
    dispatch_resume(_timer);
    //如果要退出GCD的定時(shí)器可以使用
    //dispatch_source_cancel(_timer);
}
@end

注意,如果需要暫停GCD定時(shí)器要门,則調(diào)用dispatch_suspend(_timer);虏肾,恢復(fù)則調(diào)用dispatch_resume(_timer);

NSTimer不準(zhǔn)時(shí)的原因:
1.RunLoop循環(huán)處理總會(huì)有時(shí)間
2.受到RunLoop模式的影響

GCD Timer與NSTimer是不同的
1.都是源,前者是Dispatch的源欢搜,后者是RunLoop的源
2.GCD Timer不需要像NSTimer那樣加入RunLoop的mode

GCD Timer總結(jié)
1.GCD Timer精度高(納米級(jí)別)
2.GCD Timer在主線程執(zhí)行會(huì)受RunLoop影響封豪,子線程不受影響
3.GCD Timer不受模式切換的影響

備注:

關(guān)于時(shí)間的一些宏

#define NSEC_PER_SEC 1000000000ull

#define USEC_PER_SEC 1000000ull

#define NSEC_PER_USEC 1000ull

NSEC:納秒。

USEC:微秒炒瘟。

SEC:秒

PER:每

1 吹埠、NSEC_PER_SEC,每秒有多少納秒。

2 缘琅、USEC_PER_SEC粘都,每秒有多少毫秒。(注意是指在納秒的基礎(chǔ)上)

3 胯杭、NSEC_PER_USEC驯杜,每毫秒有多少納秒做个。

七、dispatch_source_t

dispatch_source_t還有更多更多的用法滚局,我們介紹最常用的一種,使用DISPATCH_SOURCE_TYPE_DATA_ADD最住,我們可以使用聯(lián)結(jié)來實(shí)現(xiàn)一些神奇的功能

聯(lián)結(jié)的大致流程:在任一線程上調(diào)用它的一個(gè)函數(shù) dispatch_source_merge_data 后,會(huì)執(zhí)行 Dispatch Source 事先定義好的句柄茂翔,而且我們可以在這個(gè)句柄中使用dispatch_source_get_data獲取到dispatch_source_merge_data傳遞給我們的值

下面是一個(gè)簡(jiǎn)單的示例:

- (void)viewDidLoad{
    [super viewDidLoad];
    self.queue = dispatch_queue_create("add", NULL);
    self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, self.queue);
    dispatch_source_set_event_handler(self.source, ^{
        unsigned long data = dispatch_source_get_data(self.source);
        NSLog(@"響應(yīng)混蔼,得到值為:%lu",data);
    });
    dispatch_resume(self.source);
    
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    dispatch_source_merge_data(self.source, 1);
}

從上面示例我們可以想到哪個(gè)使用場(chǎng)景呢?
在我們分片下載一個(gè)大文件時(shí)珊燎,我們可以把各個(gè)分片在異步并發(fā)下載惭嚣,下載時(shí)使用dispatch_source_merge_data回傳我們的進(jìn)度,然后再句柄回調(diào)中將各個(gè)進(jìn)度相加俐末,這就可以計(jì)算出我們的下載總進(jìn)度了

更多關(guān)于dispatch_source_t的用法可以參考大神的iOS多線程——Dispatch Source

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末料按,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子卓箫,更是在濱河造成了極大的恐慌载矿,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異闷盔,居然都是意外死亡弯洗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門逢勾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牡整,“玉大人,你說我怎么就攤上這事溺拱√颖矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵迫摔,是天一觀的道長(zhǎng)沐扳。 經(jīng)常有香客問我,道長(zhǎng)句占,這世上最難降的妖魔是什么沪摄? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮纱烘,結(jié)果婚禮上杨拐,老公的妹妹穿的比我還像新娘。我一直安慰自己擂啥,他們只是感情好哄陶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啤它,像睡著了一般奕筐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上变骡,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天离赫,我揣著相機(jī)與錄音,去河邊找鬼塌碌。 笑死渊胸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的台妆。 我是一名探鬼主播翎猛,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼接剩!你這毒婦竟也來了切厘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤懊缺,失蹤者是張志新(化名)和其女友劉穎疫稿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡遗座,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年舀凛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片途蒋。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猛遍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出号坡,到底是詐尸還是另有隱情懊烤,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布筋帖,位于F島的核電站奸晴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏日麸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一逮光、第九天 我趴在偏房一處隱蔽的房頂上張望代箭。 院中可真熱鬧,春花似錦涕刚、人聲如沸嗡综。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽极景。三九已至,卻和暖如春驾茴,著一層夾襖步出監(jiān)牢的瞬間盼樟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工锈至, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晨缴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓峡捡,卻偏偏與公主長(zhǎng)得像击碗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子们拙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

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