一稀轨、概念與類型
對(duì)于GCD來說填物,所有的執(zhí)行都放到隊(duì)列中(queue)冰单,隊(duì)列的特點(diǎn)是FIFO(先提交的先執(zhí)行)幌缝。
GCD的隊(duì)列分為幾種,主隊(duì)列(main),全局隊(duì)列(global),用戶創(chuàng)建隊(duì)列(create)
對(duì)于全局隊(duì)列诫欠,默認(rèn)有四個(gè)涵卵,分為四個(gè)優(yōu)先級(jí)
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
DISPATCH_QUEUE_PRIORITY_HIGH :優(yōu)先級(jí)最高浴栽,在default,和low之前執(zhí)行
DISPATCH_QUEUE_PRIORITY_DEFAULT 默認(rèn)優(yōu)先級(jí),在low之前轿偎,在high之后
DISPATCH_QUEUE_PRIORITY_LOW 在high和default后執(zhí)行
DISPATCH_QUEUE_PRIORITY_BACKGROUND:提交到這個(gè)隊(duì)列的任務(wù)會(huì)在high優(yōu)先級(jí)的任務(wù)和已經(jīng)提交到background隊(duì)列的執(zhí)行完后執(zhí)行典鸡。官方文檔:(the queue is scheduled for execution after all high priority queues have been scheduled and the system runs items on a thread whose priority is set for background status.)
二、dispatch_async與dispatch_sync
- dispatch_sync
提交到隊(duì)列中同步執(zhí)行 - dispatch_async
提交到隊(duì)列中異步執(zhí)行坏晦,立即返回 - dispatch_barrier_sync
- dispatch_barrier_async
在隊(duì)列中萝玷,柵欄塊必須單獨(dú)執(zhí)行,不能與其它塊并行昆婿。這只對(duì)隊(duì)列有意義球碉,因?yàn)榇嘘?duì)列中的塊總是按順序逐個(gè)來執(zhí)行的。并發(fā)隊(duì)列中如果發(fā)現(xiàn)接下來要處理的塊是個(gè)柵欄塊(barrier block),那么就一直等到當(dāng)前所有并發(fā)塊都執(zhí)行完畢挖诸,才會(huì)單獨(dú)執(zhí)行這個(gè)柵欄塊汁尺。
這是Effective Objective-C 2.0 這本書 中對(duì)dispatch_barrier_async的說明法精,書中有一段示例代碼多律,大概是這樣的:
- (instancetype)init
{
self = [super init];
if (!self) {
return nil;
}
_concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
return self;
}
- (NSString *)name
{
__block NSString *name;
dispatch_sync(_concurrentQueue, ^{
name = _name;
});
return name;
}
- (void)setName:(NSString *)name
{
dispatch_barrier_async(_concurrentQueue, ^{
_name = name;
});
}
問題來了,以上代碼能實(shí)現(xiàn)對(duì)name屬性的讀寫同步嗎搂蜓?
按道理狼荞,是可以的。但事實(shí)并不可以帮碰,因?yàn)樵谶@里蘋果給我們挖了一個(gè)坑相味。。殉挽。
原因就是 在這里 我們使用的是全局并發(fā)隊(duì)列丰涉。。
都是 并發(fā)隊(duì)列 憑啥就不一樣呢斯碌?一死?
就是說,柵欄塊應(yīng)該在自己創(chuàng)建的并行隊(duì)列里執(zhí)行傻唾,如果是在串行隊(duì)列或是全局并行隊(duì)列中執(zhí)行投慈,那么就起不到柵欄的作用,和dispatch_async 函數(shù)效果一樣了冠骄。
所以上面代碼只要把_concurrentQueue 改成自己創(chuàng)建的
_concurrentQueue = dispatch_queue_create("com.people.test", DISPATCH_QUEUE_CONCURRENT);
就可以實(shí)現(xiàn) 讀寫的同步了伪煤。
隔離隊(duì)列
再來理解一個(gè)隔離隊(duì)列的概念,也是跟dispatch_barrier_sync和dispatch_barrier_async有關(guān)凛辣。
例如操作系統(tǒng)中的經(jīng)典的讀者-寫者問題抱既,簡而言之,我們可以在同一時(shí)刻有多個(gè)讀者扁誓,但同一時(shí)刻只能有一個(gè)線程可以寫入防泵。解決方法就是利用GCD的四種 APIs
- dispatch_sync
- dispatch_async
- dispatch_barrier_sync
- dispatch_barrier_async
我們的想法是讀操作可以支持同步和異步阳堕,而寫操作也能支持異步寫入,并且必須確保是寫入的是同一個(gè)引用择克。GCD 的 barrier 集合 APIs 提供了解決方案:他們將會(huì)一直等到隊(duì)列中的任務(wù)清空恬总,才會(huì)繼續(xù)執(zhí)行 block。使用 barrier APIs 可以用來限制我們對(duì)字典對(duì)象的寫入肚邢,并且確保我們不會(huì)在同一時(shí)刻進(jìn)行多個(gè)寫操作壹堰,以及正在寫操作時(shí)同時(shí)進(jìn)行讀操作÷夂看一段代碼:
class IdentityMap<T: Identifiable> {
var dictionary = Dictionary<String, T>()
let accessQueue = dispatch_queue_create("com.khanlou.isolation.queue", DISPATCH_QUEUE_CONCURRENT)
func object(withID ID: String) -> T? {
var result: T? = nil
dispatch_sync(accessQueue, {
result = dictionary[ID] as T?
})
return result
}
func addObject(object: T) {
dispatch_barrier_async(accessQueue, {
dictionary[object.ID] = object
})
}
}
dispatch_sync 將會(huì)分發(fā) block 到我們的隔離隊(duì)列上然后等待其執(zhí)行完畢贱纠。通過這種方式,我們就實(shí)現(xiàn)了同步讀操作(如果我們搞成異步讀取响蕴,getter 方法就需要一個(gè) completion block)谆焊。因?yàn)?accessQueue 是并發(fā)隊(duì)列,這些同步讀取操作可以并發(fā)執(zhí)行浦夷,也就是允許同時(shí)讀廷臼。
dispatch_barrier_async 將分發(fā) block 到隔離隊(duì)列上携茂,async 異步部分意味著會(huì)立即返回,并不會(huì)等待 block 執(zhí)行完畢。這對(duì)性能是有好處的牙咏,但是在一個(gè)寫操作后立即執(zhí)行一個(gè)讀操作會(huì)導(dǎo)致讀到一個(gè)半成品的數(shù)據(jù)(因?yàn)榭赡軐懖僮鬟€未完成就開始讀了)掩驱。
dispatch_barrier_async 中的 barrier 部分意味著:只要 barrier block 進(jìn)入隊(duì)列撒轮,并不會(huì)立即執(zhí)行巩掺,而是會(huì)等待該隊(duì)列其他 block 執(zhí)行完畢后再執(zhí)行。所以在這點(diǎn)上就保證了我們的 barrier block 每次都只有它自己在執(zhí)行续膳。而所有在他之后提交的 block 也會(huì)一直等待這個(gè) barrier block 執(zhí)行完再執(zhí)行改艇。
傳入 dispatch_barrier_async() 函數(shù)的 queue,必須是用 dispatch_queue_create 創(chuàng)建的并發(fā) queue坟岔。如果是串行 queue 或者是 global concurrent queues谒兄,這個(gè)函數(shù)就會(huì)變成 dispatch_async() 了