很多編程語(yǔ)言都會(huì)有多線程編程朗儒,拋開(kāi)多線程編程的復(fù)雜性颊乘,它確實(shí)能夠提升程序執(zhí)行的效率
参淹。特別是現(xiàn)在CPU都是多核,能夠充分發(fā)揮多核的優(yōu)勢(shì)也是一些編程語(yǔ)言的追求乏悄,比如說(shuō)golang浙值,
熟悉Golang或者java的開(kāi)發(fā)者,應(yīng)該都對(duì)多線程很熟悉檩小,然而在objc中开呐,使用GCD來(lái)進(jìn)行多線
程的編碼要來(lái)得更優(yōu)雅、更簡(jiǎn)單规求,下來(lái)就來(lái)揭開(kāi)其神秘面紗筐付。
API
開(kāi)發(fā)者要做的只是將想執(zhí)行的任務(wù)追加到適當(dāng)?shù)腄ispatch Queue中去。
dispatch_async(queue, ^{
/*
*想執(zhí)行的任務(wù)
*/
})
上面的代碼是使用GCD的一般格式阻肿,其中的queue分為兩種:
1. Serial Dispatch Queue (串行隊(duì)列)
2. Concurrent Dispatch Queue (并行隊(duì)列)
很好理解瓦戚,串行隊(duì)列中的任務(wù)會(huì)在一個(gè)線程中串行執(zhí)行,并行隊(duì)列中的任務(wù)會(huì)在多個(gè)線程中
并行執(zhí)行冕茅。那么如何得到這兩個(gè)隊(duì)列呢?
1. dispatch_queue_create
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.serialDispatchQueue", Operation)
上述Operation指示生成隊(duì)列的類(lèi)型蛹找,NULL和DISPATCH_QUEUE_SERIAL為串行姨伤,DISPATCH_QUEUE_CONCURRENT
為并行。
2. 獲取系統(tǒng)提供的隊(duì)列
_queue = dispatch_get_main_queue();
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
第一個(gè)為主線程隊(duì)列庸疾,是串行的乍楚,后四個(gè)為全局并行隊(duì)列,可以通過(guò)參數(shù)來(lái)區(qū)分其優(yōu)先級(jí)
上面總結(jié)了GCD的最基本的用法届慈,當(dāng)然還有很多很實(shí)用的API徒溪,如下:
1. dispatch_set_target_queue
可以變更Queue的優(yōu)先級(jí),還可以將多個(gè)queue中的任務(wù)歸并到某一個(gè)queue中金顿。
2. dispatch_after
可以延遲某個(gè)任務(wù)的執(zhí)行臊泌,但是要注意:這里的延遲并不是任務(wù)在指定時(shí)間之后執(zhí)行,而是
延遲指定時(shí)間追加到隊(duì)列中去揍拆。
3. dispatch_group_async
如果想要追加到queue中的多個(gè)處理結(jié)束后進(jìn)行結(jié)束處理渠概,是使用dispatch_group的絕佳場(chǎng)景。
當(dāng)然將這些任務(wù)依次放入一個(gè)串行隊(duì)列中就可以解決問(wèn)題嫂拴,但是使用并行隊(duì)列時(shí)播揪,就需要
使用dispatch group了。
4. dispatch_barrier_asyc
dispatch_barrier允許在一個(gè)并發(fā)隊(duì)列中創(chuàng)建一個(gè)同步點(diǎn)筒狠,當(dāng)在并發(fā)隊(duì)列中遇到一個(gè)barrier猪狈,
它會(huì)等到在這個(gè)barrier之前提交的所有任務(wù)都執(zhí)行完畢之后,再執(zhí)行辩恼,而所有在barrier之
后提交的任務(wù)會(huì)等到barrier之后再執(zhí)行雇庙。
5. dispatch_semaphore
是GCD的同步信號(hào)量谓形,
上面所說(shuō)的都是異步GCD的API,當(dāng)然還有一些同步的API可以使用状共,也很重要套耕。
1. dispatch_apply
該函數(shù)按指定的次數(shù)將指定的block加入到指定的queue中去,并等待全部處理執(zhí)行結(jié)束峡继。
由于此API是同步的冯袍,所以一般在dispatch_async中使用它比較常見(jiàn)。
2. dispatch_once
函數(shù)保證在應(yīng)用程序中只執(zhí)行一次任務(wù)碾牌,普遍應(yīng)用于單例對(duì)象的初始化康愤。
3. dispatch_semaphore
GCD中的同步信號(hào)量,能夠比dispatch_group等提供更細(xì)粒度的同步控制舶吗,使用很廣泛征冷。
GCD使用案例
如果多個(gè)線程同時(shí)操作(讀寫(xiě))一個(gè)可變?nèi)萜鳎秃苡锌赡軙?huì)出現(xiàn)線程安全的問(wèn)題誓琼,當(dāng)一個(gè)線程
正在讀取時(shí)另一個(gè)線程正在修改就是一個(gè)不安全的行為检激,例如:
- (void)addObject:(NSObject *)obj {
if (obj) {
[mutableArray addObject: obj];
dispatch_async(dispatch_get_main_queue(), ^{
[self postContentAddedNotification];
});
}
}
- (NSArray *)objects {
return [NSArray arrayWithArray:mutableArray];
}
如果使用GCD來(lái)改寫(xiě)這段不太安全的代碼,效果將是這樣的腹侣。
- (void)addObject:(NSObject *)obj {
if (obj) {
dispatch_barrier_async(self.concurrentQueue, ^{
[mutableArray addObject:obj];
dispatch_async(dispatch_get_main_queue(), ^{
[self postContentAddedNotification];
});
});
}
}
- (NSArray *)objects {
__block NSArray *array;
dispatch_sync(self.concurrentQueue, ^{
array = [NSArray arrayWithArray:_mutableArray];
});
return array;
}
這段代碼中叔收,寫(xiě)入數(shù)據(jù)通過(guò)一個(gè)barrier來(lái)完成,因?yàn)閎arrierBlock永遠(yuǎn)不會(huì)和其它Block一
起執(zhí)行傲隶,所以保證了寫(xiě)安全饺律。在讀的時(shí)候,使用同步調(diào)用跺株,確保了函數(shù)返回复濒。在寫(xiě)客戶(hù)端代
碼的時(shí)候不會(huì)像服務(wù)端那樣變態(tài)地去考慮多線程問(wèn)題,但是在編碼過(guò)程中意識(shí)到哪些地方可能
會(huì)出錯(cuò)還是很重要的乒省。
死鎖
GCD相當(dāng)好用巧颈,但用不好就會(huì)死鎖,始終要記著這樣一句秘籍: 不要在串行隊(duì)列放dispatch_sync、
dispatch_apply袖扛,比如:
- 案例一
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"test");
});
- 案例二
//queue為串行隊(duì)列
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"1"); // 任務(wù)1
});
NSLog(@"2"); // 任務(wù)2
});
上文中兩個(gè)死鎖的例子對(duì)于dispatch_apply同樣適用洛二。