GCD整理(二)

這篇會(huì)整理GCD常用的API

目錄
1擒悬、dispatch_after
2摊唇、dispatch_apply
3左医、dispatch_barrier_async
4、Dispatch Semaphore
5特姐、Dispatch Group

dispatch_after

看下面一段代碼:

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"hey");
});

dispatch_time_t類型變量作為dispatch_after函數(shù)的第一個(gè)參數(shù)晶丘,可以由dispatch_time和dispatch_walltime函數(shù)創(chuàng)建。先來(lái)說(shuō)前者唐含。

dispatch_time函數(shù)接收兩個(gè)參數(shù)浅浮,它會(huì)返回從第一個(gè)參數(shù)指定的時(shí)間開(kāi)始,到第二個(gè)參數(shù)指定的以毫微秒為單位的時(shí)間間隔后的時(shí)間觉壶。比如上面代碼中就是從現(xiàn)在開(kāi)始脑题,3秒后的時(shí)間件缸。

dispatch_after會(huì)將任務(wù)在指定時(shí)間后加入到執(zhí)行隊(duì)列铜靶,n * NSEC_PER_SEC會(huì)得到一個(gè)單位是毫微秒的數(shù)值,要表達(dá)3秒需要寫成3 * NSEC_PER_SEC他炊,直接寫3是不行的争剿。如果需要表示毫秒,可以使用NSEC_PER_MSEC痊末,比如100毫秒寫成100 * NSEC_PER_MSEC蚕苇。

dispatch_walltime用于指定絕對(duì)時(shí)間,比如要指定時(shí)間為2011年11月11日11時(shí)11分11秒凿叠,可以使用如下方法做NSDate到dispatch_time_t的轉(zhuǎn)換涩笤。

+ (dispatch_time_t)getDispatchTimeByDate:(NSDate *)date{
    NSTimeInterval interval;
    CGFloat second, subsecond;
    struct timespec time;
    dispatch_time_t milestone;
    
    interval = [date timeIntervalSince1970];
    subsecond = modf(interval, &second);
    time.tv_sec = second;
    time.tv_nsec = subsecond * NSEC_PER_SEC;
    milestone = dispatch_walltime(&time, 0);
    return milestone;
}

關(guān)于dispatch_after方法的參數(shù):

dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block) 參數(shù)1:執(zhí)行的時(shí)間。參數(shù)2:執(zhí)行的隊(duì)列盒件。參數(shù)3:任務(wù)蹬碧。

另外,因?yàn)閐ispatch_after并不是在指定時(shí)間后執(zhí)行炒刁,而是指定時(shí)間后加入隊(duì)列恩沽,所示任務(wù)執(zhí)行的具體時(shí)間未必會(huì)準(zhǔn)確按照time參數(shù)。

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC);
int i = 1;
while (i < 1000000000) {
    i++;
}
dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"hey");
});

比如這段代碼翔始,因?yàn)檠h(huán)和log都在主線程執(zhí)行罗心,循環(huán)會(huì)執(zhí)行很久里伯,所以0.5秒后輸出語(yǔ)句并沒(méi)有執(zhí)行,只是加入到隊(duì)列中渤闷,等到循環(huán)結(jié)束才輸出“hey”疾瓮。

如果只是簡(jiǎn)單的使用可以直接使用系統(tǒng)提供的代碼塊來(lái)快速生成代碼。

dispatch_after代碼塊.png

dispatch_apply

dispatch_apply會(huì)按照給定的次數(shù)在指定隊(duì)列中重復(fù)執(zhí)行任務(wù)肤晓,注意它是dispatch_sync和Dispatch Group的關(guān)聯(lián)API爷贫,為什么要注意這個(gè)等下就會(huì)看到。

NSArray *arr = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9"];
dispatch_apply(arr.count, dispatch_get_main_queue(), ^(size_t index) {
        NSLog(@"---%@", arr[index]);
    });

在主線程中運(yùn)行這段代碼补憾,會(huì)發(fā)現(xiàn)不會(huì)有任何輸出并且程序也會(huì)卡死漫萄,沒(méi)錯(cuò)死鎖了。dispatch_apply函數(shù)和dispatch_sync相同盈匾,都會(huì)等待任務(wù)執(zhí)行結(jié)束腾务,也就意味著dispatch_apply也是一個(gè)同步方法。在死鎖的判斷中削饵,可以把它理解為一個(gè)sync函數(shù)岩瘦,這樣就明白為什么這里會(huì)發(fā)生死鎖了(關(guān)于死鎖的問(wèn)題在GCD整理(一))。

另外如果dispatch_apply函數(shù)指定了串行隊(duì)列作為參數(shù)窿撬,那么遍歷會(huì)按照順序執(zhí)行启昧,如果是并發(fā)隊(duì)列,執(zhí)行順序則不受控制劈伴。因?yàn)榇嘘?duì)列只對(duì)應(yīng)一條線程密末,并發(fā)隊(duì)列會(huì)對(duì)應(yīng)多個(gè)線程。

代碼和效果如下:

NSArray *arr = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9"];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_apply(arr.count, queue, ^(size_t index) {
    NSLog(@"---%@", arr[index]);
});
dispatch_apply串行隊(duì)列.png
NSArray *arr = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9"];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(arr.count, queue, ^(size_t index) {
    NSLog(@"---%@", arr[index]);
});
dispatch_apply并行隊(duì)列.png

dispatch_barrier_async

提供這樣一種功能跛璧,它將dispatch_barrier_async函數(shù)之前加入隊(duì)列的任務(wù)和dispatch_barrier_async之后加入隊(duì)列的任務(wù)分割開(kāi)严里。并且dispatch_barrier_async的block會(huì)等待前面的任務(wù)執(zhí)行完成后在執(zhí)行,同時(shí)block執(zhí)行完成之前即使是并發(fā)隊(duì)列后面的任務(wù)也不會(huì)執(zhí)行追城。

dispatch_barrier_async執(zhí)行邏輯.png

一個(gè)典型的例子是使用dispatch_barrier_async處理讀寫問(wèn)題刹碾,在讀寫數(shù)據(jù)庫(kù)表的操作中,如果單純的使用dispatch_async函數(shù)執(zhí)行寫操作并且并發(fā)隊(duì)列中存在多個(gè)寫任務(wù)座柱,那么寫入的數(shù)據(jù)很有可能是錯(cuò)誤的迷帜。我們只希望同一時(shí)間只有一個(gè)任務(wù)在操作表。

另外一個(gè)情況色洞,當(dāng)任務(wù)1-2要執(zhí)行讀取戏锹,讀取后任務(wù)3要進(jìn)行寫入,寫入完成后任務(wù)4再次讀取拿到新寫入的數(shù)據(jù)锋玲。這樣的情況使用dispatch_barrier_async就會(huì)很方便景用。

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"read1");
});
dispatch_async(queue, ^{
    NSLog(@"read2");
});
dispatch_barrier_async(queue, ^{
    NSLog(@"write");
});
dispatch_async(queue, ^{
    NSLog(@"read4");
});

Dispatch Semaphore

信號(hào)量的作用和dispatch_barrier_async有些類似,它可以更加細(xì)致的控制數(shù)據(jù)的線程安全性。

描述信號(hào)量的概念可以生動(dòng)的用廁所來(lái)比喻伞插,初始值為1的信號(hào)量就像只有一個(gè)坑的廁所割粮,初始值為2就是兩個(gè)坑的廁所。每當(dāng)進(jìn)去一個(gè)人就會(huì)把門鎖上媚污,信號(hào)量就減1舀瓢;出來(lái)的時(shí)候自然會(huì)把門打開(kāi),信號(hào)量就加1耗美。如果廁所所有的坑都被占了京髓,后面來(lái)的人就只能等待。

可以用下面的語(yǔ)法創(chuàng)建一個(gè)信號(hào)量:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

dispatch_semaphore_create接受一個(gè)長(zhǎng)整形參數(shù)商架,這段代碼就好比創(chuàng)建了一個(gè)只有一個(gè)坑的廁所堰怨。可以用下面的語(yǔ)句來(lái)描述一個(gè)人占據(jù)了一個(gè)坑位的情況蛇摸。

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_semaphore_wait(semaphore, time);

dispatch_semaphore_wait接受兩個(gè)參數(shù)备图,參數(shù)1:對(duì)應(yīng)的信號(hào)量。參數(shù)2:超時(shí)時(shí)間赶袄。這個(gè)函數(shù)有一個(gè)長(zhǎng)整形返回值揽涮,返回值為0表示信號(hào)量的值大于等于1,或者在指定的等待時(shí)間內(nèi)饿肺,超時(shí)返回非0蒋困。

引用一段別人對(duì)該函數(shù)的描述。

如果信號(hào)量的值大于0敬辣,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語(yǔ)句雪标,并且將信號(hào)量的值減1;如果信號(hào)量的值為0购岗,那么這個(gè)函數(shù)就阻塞當(dāng)前線程等待timeout汰聋,如果等待的期間信號(hào)量的值被dispatch_semaphore_signal函數(shù)加1了门粪,且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號(hào)量喊积,那么就繼續(xù)向下執(zhí)行并將信號(hào)量減1。如果等待期間沒(méi)有獲取到信號(hào)量或者信號(hào)量的值一直為0玄妈,那么等到timeout時(shí)乾吻,其所處線程自動(dòng)執(zhí)行其后語(yǔ)句。參考鏈接

有減就一定有加拟蜻,dispatch_semaphore_signal就是對(duì)應(yīng)從廁所開(kāi)門出來(lái)那段劇情的函數(shù)绎签。這個(gè)函數(shù)會(huì)把傳入的信號(hào)量加1,它同樣有返回值酝锅,0代表當(dāng)前并沒(méi)有等待中的線程需要被喚醒诡必;非0代表當(dāng)前有至少一個(gè)等待中的線程,并且成功喚醒了其中一個(gè)。

運(yùn)行一段代碼看下效果:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);

for (NSInteger i = 0; i < 3; i++) {
    dispatch_async(queue, ^{
        NSInteger wait = dispatch_semaphore_wait(semaphore, time);
        NSLog(@"wait:%ld-%@", wait, [NSThread currentThread]);
        int i = 1;
        while (i < 1000000000) {
            i++;
        }
        NSLog(@"finish");
        NSInteger singal = dispatch_semaphore_signal(semaphore);
        NSLog(@"singal:%ld-%@", singal, [NSThread currentThread]);
    });
}
dispatch_semaphore運(yùn)行結(jié)果.png

分析代碼的運(yùn)行邏輯:number=2的線程最先拿到了信號(hào)量爸舒,并將信號(hào)量減1蟋字,與此同時(shí)3、4線程也運(yùn)行到了這里并且開(kāi)始等待扭勉。在等待過(guò)程中2線程將循環(huán)執(zhí)行完畢并調(diào)用dispatch_semaphore_signal函數(shù)鹊奖,dispatch_semaphore_signal發(fā)現(xiàn)還有兩個(gè)線程在等待中,有線程在等待就返回非0(這里為1)涂炎;同時(shí)線程3開(kāi)始拿到信號(hào)量并執(zhí)行循環(huán)忠聚。這時(shí)候指定的等待時(shí)間3秒已經(jīng)到了,線程4不能再等了唱捣,于是開(kāi)始執(zhí)行循環(huán)两蟀。這就意味著線程3和4在某一段時(shí)間內(nèi)是同時(shí)執(zhí)行循環(huán)操作的。隨后各自執(zhí)行dispatch_semaphore_signal函數(shù)震缭,此時(shí)已經(jīng)沒(méi)有等待線程存在所以返回0垫竞。

再放一個(gè)例子:

NSMutableArray *array = [[NSMutableArray alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

for (NSInteger i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        [array addObject:@(i)];
        dispatch_semaphore_signal(semaphore);
    });
}

因?yàn)镹SMutableArray是非線程安全的,如果同一時(shí)間有多個(gè)線程對(duì)array數(shù)組進(jìn)行addObject操作一定會(huì)發(fā)生異常蛀序』兜桑可以使用信號(hào)量來(lái)控制同一時(shí)間只有一條線程執(zhí)行addObject操作。

Dispatch Group

Dispatch Group 可以處理這樣一種情況:一個(gè)或多個(gè)并發(fā)隊(duì)列中有很多任務(wù)在執(zhí)行徐裸,而有一個(gè)任務(wù)必須在所有任務(wù)都執(zhí)行完成后最后執(zhí)行遣鼓。比如要上傳多張圖片到后臺(tái),在全部上傳完成后要告知用戶圖片上傳完成重贺。

這時(shí)Dispatch Group就會(huì)發(fā)揮極大的作用:

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_async(group, queue1, ^{
    NSLog(@"queue1 hello:%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue1, ^{
    NSLog(@"queue1 world:%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue2, ^{
    NSLog(@"queue2 hello:%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue2, ^{
    NSLog(@"queue2 world:%@", [NSThread currentThread]);
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"finish:%@", [NSThread currentThread]);
});

dispatch_group_create函數(shù)用來(lái)創(chuàng)建一個(gè) Dispatch Group骑祟。dispatch_group_async用法與dispatch_async一樣,只是多了個(gè)group參數(shù)气笙。dispatch_group_notify函數(shù)會(huì)在group里的block全部執(zhí)行完成后運(yùn)行自己的block次企。

Dispatch Group還有一個(gè)很重要的方法:

dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); 指定Dispatch Group的等待時(shí)間。參數(shù)1:組潜圃。參數(shù)2:超時(shí)時(shí)間缸棵。函數(shù)有一個(gè)返回值,為0表示在指定時(shí)間內(nèi)所有的block都已經(jīng)執(zhí)行完畢谭期。非0表示在指定時(shí)間內(nèi)任務(wù)沒(méi)有執(zhí)行完堵第。

還有比較常見(jiàn)的Dispatch Once,用法比較簡(jiǎn)單也不整理了隧出。

最后踏志,這篇GCD整理是閱讀《Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理》GCD部分的內(nèi)容后,結(jié)合其他大大們的博文以及自己的理解整理的一份筆記胀瞪。水平有限针余,歡迎指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市圆雁,隨后出現(xiàn)的幾起案子傍妒,更是在濱河造成了極大的恐慌,老刑警劉巖摸柄,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颤练,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驱负,警方通過(guò)查閱死者的電腦和手機(jī)嗦玖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)跃脊,“玉大人宇挫,你說(shuō)我怎么就攤上這事±沂酰” “怎么了器瘪?”我有些...
    開(kāi)封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)绘雁。 經(jīng)常有香客問(wèn)我橡疼,道長(zhǎng),這世上最難降的妖魔是什么庐舟? 我笑而不...
    開(kāi)封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任欣除,我火速辦了婚禮,結(jié)果婚禮上挪略,老公的妹妹穿的比我還像新娘历帚。我一直安慰自己,他們只是感情好杠娱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布挽牢。 她就那樣靜靜地躺著,像睡著了一般摊求。 火紅的嫁衣襯著肌膚如雪禽拔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天睹簇,我揣著相機(jī)與錄音奏赘,去河邊找鬼寥闪。 笑死太惠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疲憋。 我是一名探鬼主播凿渊,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了埃脏?” 一聲冷哼從身側(cè)響起搪锣,我...
    開(kāi)封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎彩掐,沒(méi)想到半個(gè)月后构舟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡堵幽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年狗超,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朴下。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡努咐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出殴胧,到底是詐尸還是另有隱情渗稍,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布团滥,位于F島的核電站竿屹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灸姊。R本人自食惡果不足惜羔沙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厨钻。 院中可真熱鬧扼雏,春花似錦、人聲如沸夯膀。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诱建。三九已至蝴蜓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俺猿,已是汗流浹背茎匠。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留押袍,地道東北人诵冒。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谊惭,于是被迫代替她去往敵國(guó)和親汽馋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子侮东,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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

  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時(shí)候我都想好了最壞的可能(胃癌)豹芯,之前在網(wǎng)上查的癥狀都很相似悄雅。...
    Dely閱讀 9,237評(píng)論 21 42
  • 一握牧、多線程簡(jiǎn)介: 所謂多線程是指一個(gè) 進(jìn)程 -- process(可以理解為系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序)中可以開(kāi)...
    尋形覓影閱讀 1,030評(píng)論 0 6
  • “他~杜拉拉升職記深深的吸引了我便锨,默默關(guān)注,緣分使然我碟,與你相見(jiàn)放案。你~深邃的眼神,富有磁性的話語(yǔ)矫俺,德藝雙馨的人格魅力...
    我是行走閱讀 324評(píng)論 0 0
  • 這樣每天都有點(diǎn)進(jìn)展的節(jié)奏吱殉,感覺(jué)很穩(wěn),很有鼓勵(lì)的方式在支持我向前走厘托,雖然還有創(chuàng)業(yè)的孤獨(dú)友雳,但是已經(jīng)不重要了! 1.大學(xué)...
    一百八十斤大胖子閱讀 223評(píng)論 0 1
  • 感覺(jué) 該做些什么 嗯 或是說(shuō) 創(chuàng)造些什么 今天和某人看火星情報(bào)局 宇哥說(shuō)人越是成熟 越不會(huì)輕易承諾 除非能保證兌現(xiàn)...
    從沒(méi)想到會(huì)戀上白水啊閱讀 122評(píng)論 0 0