GCD編程的核心就是dispatch隊列,dispatch block的執(zhí)行最終都會放進(jìn)某個隊列中去進(jìn)行蛛勉。gcd中相關(guān)函數(shù)的使用一般都是以dispatch開頭
一、函數(shù)和隊列
1、GCD 中的兩個常用函數(shù)
1讥珍、異步函數(shù)(async): 可以在新的線程中執(zhí)行任務(wù), 具備開啟新線程的能力;
2、同步函數(shù)(sync): 只能在當(dāng)前線程中執(zhí)行任務(wù), 不具備開新啟線程的能力窄瘟;
queue: 隊列 bolck: 任務(wù)
異步函數(shù)
dispatch_async(dispatch_queue_t queue, ^{
// block 內(nèi)容
});
同步函數(shù)
dispatch_sync(dispatch_queue_t queue, ^{
// block 內(nèi)容
});
2衷佃、GCD的隊列類型
1、并發(fā)隊列(Concurrent Dispatch Queue)
2蹄葱、串行隊列(Serial Dispatch Queue)
a.并發(fā)隊列:可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))氏义,并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效。
(并發(fā)的意思是可以同時執(zhí)行第一個任務(wù) 但是是時間是分片的图云,每刻只有一個任務(wù)執(zhí)行 就是執(zhí)行這個任務(wù)一會 再執(zhí)行另外任務(wù) 交替進(jìn)行的)
創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
b.串行隊列:讓任務(wù)一個接著一個的執(zhí)行 (一個任務(wù)執(zhí)行完畢再執(zhí)行下一個任務(wù))
創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_SERIAL);
獲取主隊列
//dispatch_get_main_queue()獲得主隊列
主隊列是一種特殊串行隊列惯悠;
和其它串行隊列一樣,這個隊列中的任務(wù)一次只能執(zhí)行一個竣况。
然而克婶,它能保證所有的任務(wù)都在主線程執(zhí)行,主線程是唯一可用于更新 UI 的線程丹泉。
二情萤、GCD 的基本使用
1. 異步函數(shù)+并發(fā)隊列:開啟多條線程,并發(fā)執(zhí)行任務(wù)
2. 異步函數(shù)+串行隊列:開啟一條線程摹恨,串行執(zhí)行任務(wù)
3. 異步函數(shù)+主隊列:不開線程筋岛,在主線程中串行執(zhí)行任務(wù)
4. 同步函數(shù)+并發(fā)隊列:不開線程,串行執(zhí)行任務(wù)
5. 同步函數(shù)+串行隊列:不開線程晒哄,串行執(zhí)行任務(wù)
6. 同步函數(shù)+主隊列:不開線程睁宰,串行執(zhí)行任務(wù)(注意死鎖發(fā)生)
1.異步函數(shù)和并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
// 也可以獲取全局并發(fā)隊列,執(zhí)行效果是一樣的
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{ NSLog(@"1 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"2 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"3 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"4 %@", [NSThread currentThread]); });
結(jié)果:
4 <NSThread: 0x60000026fd00>{number = 6, name = (null)}
2 <NSThread: 0x600000270e40>{number = 4, name = (null)}
1 <NSThread: 0x600000270f00>{number = 3, name = (null)}
3 <NSThread: 0x60000026c400>{number = 5, name = (null)}
可以看出隊列開啟了4條子線程區(qū)分別執(zhí)行4個任務(wù), 隊列中的任務(wù)是并發(fā)執(zhí)行的.
但是在這里有個注意點: 并不是說有多少任務(wù), GCD就會開啟多少條線程,
具體開啟幾條線程是不確定的, 是由系統(tǒng)決定的.
2.異步函數(shù)和串行隊列
dispatch_queue_t queue = dispatch_queue_create("asyncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ NSLog(@"1 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"2 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"3 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"4 %@", [NSThread currentThread]); });
結(jié)果:
1 <NSThread: 0x60000007e400>{number = 3, name = (null)}
2 <NSThread: 0x60000007e400>{number = 3, name = (null)}
3 <NSThread: 0x60000007e400>{number = 3, name = (null)}
4 <NSThread: 0x60000007e400>{number = 3, name = (null)}
隊列只開啟了一條子線程, 去一個接著一個任務(wù)去執(zhí)行. 這種方式對任務(wù)的執(zhí)行效率沒有任何提高.
3.異步函數(shù)和主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{ NSLog(@"1 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"2 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"3 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"4 %@", [NSThread currentThread]); });
結(jié)果:
1 <NSThread: 0x60000007a600>{number = 1, name = main}
2 <NSThread: 0x60000007a600>{number = 1, name = main}
3 <NSThread: 0x60000007a600>{number = 1, name = main}
4 <NSThread: 0x60000007a600>{number = 1, name = main}
主隊列所有的任務(wù)確實是在主線程執(zhí)行的, 雖然是異步函數(shù), 但也不會開啟線程.
4.同步函數(shù)和并發(fā)隊列
//第一個參數(shù): C語言的字符串,標(biāo)簽
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{ NSLog(@"test1- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"test2- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"test3- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"test4- %@", [NSThread currentThread]); });
打印結(jié)果:
test1- <NSThread: 0x608000078dc0>{number = 1, name = main}
test2- <NSThread: 0x608000078dc0>{number = 1, name = main}
test3- <NSThread: 0x608000078dc0>{number = 1, name = main}
test4- <NSThread: 0x608000078dc0>{number = 1, name = main}
同步函數(shù)是不會開啟子線程的, 所有任務(wù)都是在主線程中串行執(zhí)行的.
5.同步函數(shù)和串行隊列
dispatch_queue_t queue = dispatch_queue_create("syncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{ NSLog(@"syncSerial-1- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncSerial-2- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncSerial-3- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncSerial-4- %@", [NSThread currentThread]); });
打印結(jié)果:
syncSerial-1- <NSThread: 0x60800007b100>{number = 1, name = main}
syncSerial-2- <NSThread: 0x60800007b100>{number = 1, name = main}
syncSerial-3- <NSThread: 0x60800007b100>{number = 1, name = main}
syncSerial-4- <NSThread: 0x60800007b100>{number = 1, name = main}
同樣, 此時也是不會創(chuàng)建子線程的, 所有任務(wù)是在主線程中也是串行執(zhí)行, 和4的情況是一樣的效果.
6.同步函數(shù)和主隊列
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{ NSLog(@"syncMain1- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncMain2- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncMain3- %@", [NSThread currentThread]); });
NSLog(@"---end---");
結(jié)果(只有這一行輸出,死鎖的情況):
---begin---
由隊列引起的循環(huán)等待揩晴。
三勋陪、GCD 常用函數(shù)
1. dispatch_barrier_async函數(shù)(柵欄函數(shù)) :
它等待所有位于barrier函數(shù)之前的操作執(zhí)行完畢后執(zhí)行, 并且在barrier函數(shù)執(zhí)行之后, barrier函數(shù)之后的操作才會得到執(zhí)行, 該函數(shù)需要同dispatch_queue_create函數(shù)生成的并發(fā)隊列(concurrent Dispatch Queue)一起使用
dispatch_barrier_async代碼實例:
//同dispatch_queue_create函數(shù)生成的concurrent Dispatch Queue隊列一起使用
dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{NSLog(@"--1--%@", [NSThread currentThread]);});
dispatch_async(queue, ^{NSLog(@"--2--%@", [NSThread currentThread]);});
dispatch_barrier_async(queue, ^{NSLog(@"--barrier--%@", [NSThread currentThread]);});
dispatch_async(queue, ^{NSLog(@"--3--%@", [NSThread currentThread]);});
dispatch_async(queue, ^{NSLog(@"--4--%@", [NSThread currentThread]);});
輸出結(jié)果:1 2 --> barrier -->3 4 其中12 與 34 由于并行處理先后順序不定
下面是你何時會,和不會硫兰,使用障礙函數(shù)的情況:
自定義串行隊列:一個很壞的選擇诅愚;障礙不會有任何幫助,因為不管怎樣,一個串行隊列一次都只執(zhí)行一個操作违孝。
全局并發(fā)隊列: 要小心刹前,這可能不是最好的主意,因為其它系統(tǒng)可能在使用隊列而且你不能壟斷它們只為你自己的目的雌桑。
自定義并發(fā)隊列:這對于原子或臨界區(qū)代碼來說是極佳的選擇喇喉。任何你在設(shè)置或?qū)嵗男枰€程安全的事物都是使用障礙的最佳候選。
2. dispatch_after函數(shù)延遲處理任務(wù):
在實際的開發(fā)中,經(jīng)常會遇到想要在指定的時間間隔后執(zhí)行某個處理
2.1 默認(rèn)在主線程中執(zhí)行的
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"GCD-%@", [NSThread currentThread]);
});
結(jié)果: GCD-<NSThread: 0x60000006b4c0>{number = 1, name = main}
2.2 可以修改任務(wù)任務(wù)執(zhí)行所在的線程. 任務(wù)是在子線程中執(zhí)行的.
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), queue, ^{
NSLog(@"GCD-%@", [NSThread currentThread]);
});
結(jié)果: GCD-<NSThread: 0x60000006a3c0>{number = 3, name = (null)}
何時適合使用 dispatch_after 比較合適校坑?
自定義串行隊列: 在一個自定義串行隊列上使用 dispatch_after 要小心拣技。你最好堅持使用主隊列。
主隊列(串行): 是使用 dispatch_after 的好選擇耍目;Xcode 提供了一個不錯的自動完成模版膏斤。
并發(fā)隊列: 在并發(fā)隊列上使用 dispatch_after 也要小心;你會這樣做就比較罕見邪驮。還是在主隊列做這些操作吧莫辨。
補充
NSObject中提供的線程延遲方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
通過NSTimer來延遲線程執(zhí)行
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
3. once 一次性執(zhí)行
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"once - %@", [NSThread currentThread]);
});
它在整個運行程序中只會執(zhí)行一次,
GCD 的一次性執(zhí)行代碼一般都是用在單例設(shè)計模式中.保證全局只有一個對象實例.
4. Group queue (隊列組):
將多線程進(jìn)行分組,最大的好處是可獲知所有線程的完成情況毅访。Group queue 可以通過調(diào)用dispatch_group_create()來獲取沮榜,通過dispatch_group_notify, 可以直接監(jiān)聽組里所有線程完成情況。
// 創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
//隊列組異步執(zhí)行任務(wù)
dispatch_group_async(group, queue, ^{ NSLog(@"任務(wù)1--%@", [NSThread currentThread]); });
dispatch_group_async(group, queue, ^{ NSLog(@"任務(wù)2--%@", [NSThread currentThread]); });
dispatch_group_async(group, queue, ^{ NSLog(@"任務(wù)3--%@", [NSThread currentThread]); });
// 隊列組攔截通知模塊(內(nèi)部本身是異步執(zhí)行的,不會阻塞線程)
dispatch_group_notify(group, queue, ^{ NSLog(@"--隊列組任務(wù)執(zhí)行完畢--"); });
打印結(jié)果:
任務(wù)2--<NSThread: 0x60800007e140>{number = 8, name = (null)}
任務(wù)1--<NSThread: 0x60800007e040>{number = 6, name = (null)}
任務(wù)3--<NSThread: 0x60800007f6c0>{number = 9, name = (null)}
--隊列租任務(wù)執(zhí)行完畢--
任務(wù)1--<NSThread: 0x60800007f6c0>{number = 9, name = (null)}
任務(wù)2--<NSThread: 0x60800007e040>{number = 6, name = (null)}
任務(wù)3--<NSThread: 0x60800007e140>{number = 8, name = (null)}
--隊列租任務(wù)執(zhí)行完畢--
任務(wù)1--<NSThread: 0x60800007e040>{number = 6, name = (null)}
任務(wù)2--<NSThread: 0x60800007e140>{number = 8, name = (null)}
任務(wù)3--<NSThread: 0x60800007f6c0>{number = 9, name = (null)}
--隊列租任務(wù)執(zhí)行完畢--
4.1 dispatch_group_enter / leave
和內(nèi)存管理的引用計數(shù)類似喻粹,我們可以認(rèn)為group也持有一個整形變量(只是假設(shè))蟆融,當(dāng)調(diào)用enter時計數(shù)加1,調(diào)用leave時計數(shù)減1守呜,當(dāng)計數(shù)為0時會調(diào)用dispatch_group_notify并且dispatch_group_wait會停止等待振愿;
NSLog(@"1");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_notify(group, queue, ^{
NSLog(@"2");
});
dispatch_group_enter(group);
dispatch_sync(queue, ^{
NSLog(@"3");
});
dispatch_group_leave(group);
輸出132
-------------------------------------------------
dispatch_group_t group =dispatch_group_create();
dispatch_queue_t globalQueue=dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
//模擬多線程耗時操作
dispatch_async(globalQueue, ^{
sleep(5);
NSLog(@"block1完成");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
//模擬多線程耗時操作
dispatch_async(globalQueue, ^{
sleep(3);
NSLog(@"block2完成");
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"全部結(jié)束");
});
block2完成
block1完成
全部結(jié)束
block里執(zhí)行的是同步類型的代碼那么用dispatch_group_async一樣可以達(dá)到同步的效果,但是異步任務(wù)就不行了如下:
dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
dispatch_async(globalQueue, ^{
sleep(3);
NSLog(@"任務(wù)一完成");
});
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
dispatch_async(globalQueue, ^{
sleep(5);
NSLog(@"任務(wù)二完成");
});
});
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"notify:任務(wù)都完成了");
});
如果dispatch_group_async里執(zhí)行的是異步代碼dispatch_group_notify會直接觸發(fā)而不會等待異步任務(wù)完成弛饭,而dispatch_group_enter冕末、和dispatch_group_leave則不會有這個問題,它們只需要在任務(wù)開始前enter結(jié)束后leave即可達(dá)到線程同步的效果侣颂。
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
dispatch_group_enter(dispatchGroup);
dispatch_async(globalQueue, ^{
sleep(5);
NSLog(@"任務(wù)二完成");
dispatch_group_leave(dispatchGroup);
});
});
4.2 dispatch_group_wait
和dispatch_group_notify功能類似(多了一個dispatch_time_t參數(shù)可以設(shè)置超時時間)档桃,在group上任務(wù)完成前,dispatch_group_wait會阻塞當(dāng)前線程(所以不能放在主線程調(diào)用)一直等待憔晒;當(dāng)group上任務(wù)完成藻肄,或者等待時間超過設(shè)置的超時時間會結(jié)束等待;
5. GCD定時器
GCD定時器的優(yōu)勢
不受RunLoop的運行模式的影響(因為它的底層也是C語言)
大家在開發(fā)的過程中拒担,經(jīng)常會用到定時器嘹屯,通常的做法可能就是NSTimer,了解過GCD的同學(xué)可能會接觸到dispatch source的概念从撼,dispatch source是一個監(jiān)視某些類型事件的對象州弟。
1.dispatch_source_create
創(chuàng)建一個新的調(diào)度源來監(jiān)視低級別的系統(tǒng)對象和自動提交處理程序塊來響應(yīng)事件調(diào)度隊列
2.dispatch_source_set_timer
為一個定時源設(shè)置一個開始時間、事件間隔、誤差值
我們來看看這個函數(shù)原型
dispatch_source_set_timer(dispatch_source_t source,
dispatch_time_t start,
uint64_t interval,
uint64_t leeway);
source當(dāng)然就是我們第一步創(chuàng)建的調(diào)度源婆翔。
start是我們設(shè)定的計時開始時間拯杠,可以dispatch_time 和 dispatch_walltime 函數(shù)來創(chuàng)建它們.
3.dispatch_source_set_event_handler
給一個調(diào)度源設(shè)置一個時間處理塊。
4.dispatch_source_cancel
異步取消一個調(diào)度源啃奴,防止任何進(jìn)一步調(diào)用它的事件處理塊的發(fā)生
5.dispatch_source_set_cancel_handler
給一個調(diào)度源設(shè)置一個取消處理塊
6.dispatch_resume
同步等待一個對象潭陪,直到超時
以上幾個步驟中,dispatch_source_set_event_handler與dispatch_source_cancel必須一起出現(xiàn)最蕾,
否則調(diào)度源處理塊不會執(zhí)行依溯。
代碼示例:
//dispatch_walltime(NULL, 0)可以換成DISPATCH_TIME_NOW
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(_timer,
dispatch_walltime(NULL, 0),
1.0*NSEC_PER_SEC,
0); //每秒執(zhí)行
dispatch_source_set_event_handler(_timer, ^{
//在這里執(zhí)行事件
dispatch_source_cancel(_timer);
});
//開始執(zhí)行dispatch源
dispatch_resume(_timer);
6、快速迭代
使用dispatch_apply函數(shù)能進(jìn)行快速迭代遍歷
dispatch_async(dispatch_get_global_queue(0, 0), ^(){
dispatch_apply(10 , dispatch_get_global_queue(0, 0), ^(size_t index){
// 執(zhí)行10次代碼瘟则,index順序不確定
NSLog(@"%ld", index);
});
NSLog(@"done");
});
輸出 index 順序不確定誓沸,因為它是并行執(zhí)行的(dispatch_get_global_queue是并行隊列),但是done是在以上操作完成后才會執(zhí)行壹粟,因此,它一般都是放在dispatch_async里面(異步)宿百。
實際上趁仙,這里 dispatch_apply如果換成串行隊列上,則會依次輸出index垦页,但這樣違背了我們想并行提高執(zhí)行效率的初衷雀费。
7、dispatch_semaphore
信號量基于計數(shù)器的一種多線程同步機(jī)制痊焊。在多個線程訪問共有資源時候盏袄,會因為多線程的特性而引發(fā)數(shù)據(jù)出錯的問題。
3個相關(guān)方法:
-
dispatch_semaphore_t dispatch_semaphore_create(long value)
方法接收一個long類型的參數(shù), 返回一個dispatch_semaphore_t類型的信號量薄啥,值為傳入的參數(shù) -
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
接收一個信號和時間值辕羽,若信號的信號量為0,則會阻塞當(dāng)前線程垄惧,直到信號量大于0或者經(jīng)過輸入的時間值刁愿;若信號量大于0,則會使信號量減1并返回到逊,程序繼續(xù)住下執(zhí)行 -
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
使信號量加1并返回
7.1:保持線程同步
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int j = 0;
dispatch_async(queue, ^{
j = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"finish j = %zd", j);
結(jié)果輸出 j = 100铣口;
如果注掉dispatch_semaphore_wait這一行,則 j = 0觉壶;
解釋
由于是將block異步添加到一個并行隊列里面脑题,所以程序在主線程躍過block直接到dispatch_semaphore_wait這一行,因為semaphore信號量為0铜靶,時間值為DISPATCH_TIME_FOREVER叔遂,所以當(dāng)前線程會一直阻塞,直到block在子線程執(zhí)行到dispatch_semaphore_signal,使信號量+1掏熬,此時semaphore信號量為1了佑稠,所以程序繼續(xù)往下執(zhí)行。這就保證了線程間同步了旗芬。
7.2:為線程加鎖
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 100; i++) {
dispatch_async(queue, ^{
// 相當(dāng)于加鎖
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"i = %zd semaphore = %@", i, semaphore);
// 相當(dāng)于解鎖
dispatch_semaphore_signal(semaphore);
});
}
注釋:當(dāng)線程1執(zhí)行到dispatch_semaphore_wait這一行時舌胶,semaphore的信號量為1,所以使信號量-1變?yōu)?疮丛,并且線程1繼續(xù)往下執(zhí)行幔嫂;如果當(dāng)在線程1NSLog這一行代碼還沒執(zhí)行完的時候,又有線程2來訪問誊薄,執(zhí)行dispatch_semaphore_wait時由于此時信號量為0履恩,且時間為DISPATCH_TIME_FOREVER,所以會一直阻塞線程2(此時線程2處于等待狀態(tài)),直到線程1執(zhí)行完NSLog并執(zhí)行完dispatch_semaphore_signal使信號量為1后呢蔫,線程2才能解除阻塞繼續(xù)住下執(zhí)行切心。以上可以保證同時只有一個線程執(zhí)行NSLog這一行代碼。
附:
GCD使用三部曲之:基本用法里面提到“掛起和恢復(fù)隊列‘’
dispatch_group的理解及使用
iOS 使用dispatch_group_enter使多次網(wǎng)絡(luò)請求依次執(zhí)行http://www.reibang.com/p/23d77601483d