介紹
GCD(Grand Central Dispatch)是基于C語言開發(fā)的一套多線程開發(fā)機(jī)制腊状,也是目前蘋果官方推薦的多線程開發(fā)方法。GCD 是一套低層API袋狞,基于C語言開發(fā)映屋,完全面向過程的,用于將任務(wù)切分成單一任務(wù)提交至隊列并發(fā)或者串行執(zhí)行。遵循FIFO原則早处,先提交到隊列的先執(zhí)行乙濒。
iOS4.0中首度引入GCD,GCD是管理任務(wù)執(zhí)行的一項技術(shù)傻丝,它使得我們對多任務(wù)處理變得更加方便和有效诉儒。它支持同步或異步任務(wù)處理,串行或并行的處理隊列(Dispath Queue)泛释,非系統(tǒng)調(diào)用的信號量機(jī)制温算,定時任務(wù)處理注竿,進(jìn)程、文件或網(wǎng)絡(luò)的監(jiān)聽任務(wù)等裙顽。這個龐大的任務(wù)處理技術(shù)大大減少了線程的管理工作宣谈,使基于任務(wù)的開發(fā)變得更加高效闻丑。
GCD 和 block (<http://scottmaxiao.github.io/IOS-Block.html>)的配合使用,可以方便地進(jìn)行多線程編程扬卷。
基本概念
串行和并行
串行:任務(wù)按先后順序逐個執(zhí)行酸钦。
并行:后面的任務(wù)不會等前面的任務(wù)完成了再執(zhí)行卑硫,同樣會遵循先添加先執(zhí)行的原則,但添加間隔往往忽略不計入挣。所以看上去像是一起執(zhí)行硝拧。
并發(fā)和并行
并發(fā)是指兩個或多個事件在同一時間間隔內(nèi)發(fā)生。單核CPU并發(fā)切換執(zhí)行多個任務(wù)滋恬。
并行是指兩個或者多個事件在同一時刻發(fā)生恢氯。多核CPU可以平行執(zhí)行多個任務(wù)。
下圖描述的就是并發(fā)和并行的區(qū)別勋磕。
同步和異步
同步:一個同步函數(shù)只在完成了預(yù)定任務(wù)后才返回敢靡。會阻塞當(dāng)前線程啸胧。
異步:異步時任務(wù)開啟會立即返回,不阻塞當(dāng)前線程去執(zhí)行下一個函數(shù)。異步會開啟其他線程柠辞。
函數(shù)說明
Dispatch Queue
Dispatch Queue是用來執(zhí)行任務(wù)的隊列主胧,是GCD中最基本的元素之一。
Dispatch Queue分為兩種:
- Serial Dispatch Queue焙格,按添加進(jìn)隊列的順序(先進(jìn)先出)一個接一個的執(zhí)行
- Concurrent Dispatch Queue眷唉,并發(fā)執(zhí)行隊列里的任務(wù)
簡而言之囤官,Serial Dispatch Queue只使用了一個線程,Concurrent Dispatch Queue使用了多個線程(具體使用了多少個肝陪,由系統(tǒng)決定)氯窍。
手動創(chuàng)建Dispatch Queue
//創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", nil);
//創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建并行隊列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_CONCURRENT);
獲取系統(tǒng)提供的Dispatch Queue
系統(tǒng)提供的Dispatch Queue有兩種類型
Main Dispatch Queue:
實際上就是Serial Dispatch Queue(并且只有一個),一般只在需要更新UI時我們才獲取Main Dispatch QueueGlobal Dispatch Queue:
實際上是一個Concurrent Dispatch Queue贝淤。大多數(shù)情況下熊楼,可以不必通過dispatch_queue_create函數(shù)生成Concurrent Dispatch Queue,而是只要獲取Global Dispatch Queue使用即可犬耻。Global Dispatch Queue有4個優(yōu)先級执泰,分別是:High、Default计济、Low沦寂、Background淘衙。
//獲取Main Dispatch Queue
dispatch_queue_t mainQueue = dispatch_get_main_queue()
//獲取Global Dispatch Queue
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
需要注意一點,如果是在OS X 10.8或iOS 6以及之后版本中使用毯侦,Dispatch Queue將會由ARC自動管理侈离,如果是在此之前的版本筝蚕,需要自己手動釋放,如下:
//創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", nil);
dispatch_async(queue, ^{
...
});
dispatch_release(queue) ;
dispatch_async和dispatch_sync,dispatch_barrier_async
dispatch_async()
dispatch_async():異步添加進(jìn)任務(wù)隊列蔗坯,它不會做任何等待宾濒。
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
[NSThread sleepForTimeInterval:5];
NSLog(@"3");
});
NSLog(@"4");
輸出
11:42:43.820 GCDSeTest[568:303] 1
11:42:43.820 GCDSeTest[568:303] 4
11:42:43.820 GCDSeTest[568:1003] 2
11:42:48.821 GCDSeTest[568:1003] 3//模擬長時間操作時間
dispatch_sync()
dispatch_sync():同步添加操作绘梦。他是等待添加進(jìn)隊列里面的操作完成之后再繼續(xù)執(zhí)行。
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_sync(concurrentQueue, ^(){
NSLog(@"2");
[NSThread sleepForTimeInterval:10];
NSLog(@"3");
});
NSLog(@"4");
輸出
11:36:25.313 GCDSeTest[544:303] 1
11:36:25.313 GCDSeTest[544:303] 2
11:36:30.313 GCDSeTest[544:303] 3//模擬長時間操作
11:36:30.314 GCDSeTest[544:303] 4
關(guān)于死鎖:
//在main線程使用“同步”方法提交Block钝诚,必定會死鎖榄棵。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"I am block...");
});
dispatch_sync在等待block語句執(zhí)行完成疹鳄,而block語句需要在主線程里執(zhí)行,所以dispatch_sync如果在主線程調(diào)用就會造成死鎖
dispatch_barrier_async
dispatch_barrier_async用于等待前面的任務(wù)執(zhí)行完畢后自己才執(zhí)行垫蛆,而它后面的任務(wù)需等待它完成之后才執(zhí)行袱饭。一個典型的例子就是數(shù)據(jù)的讀寫呛占,通常為了防止文件讀寫導(dǎo)致沖突,我們會創(chuàng)建一個串行的隊列决左,所有的文件操作都是通過這個隊列來執(zhí)行走贪,比如FMDB坠狡,這樣就可以避免讀寫沖突遂跟。不過其實這樣效率是有提升的空間的幻锁,當(dāng)沒有更新數(shù)據(jù)時,讀操作其實是可以并行進(jìn)行的假消,而寫操作需要串行的執(zhí)行岭接,如何實現(xiàn)呢:
dispatch_queue_t queue = dispatch_queue_create("Database_Queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"reading data1");
});
dispatch_async(queue, ^{
NSLog(@"reading data2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"writing data1");
[NSThread sleepForTimeInterval:1];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"reading data3");
});
我們將寫數(shù)據(jù)的操作放在dispatch_barrier_async中,這樣能確保在寫數(shù)據(jù)的時候會等待前面的讀操作完成粘拾,而后續(xù)的讀操作也會等到寫操作完成后才能繼續(xù)執(zhí)行创千,提高文件讀寫的執(zhí)行效率追驴。
dispatch_apply
dispatch_apply類似一個for循環(huán),會在指定的dispatch queue中運(yùn)行block任務(wù)n次戒良,如果隊列是并發(fā)隊列冠摄,則會并發(fā)執(zhí)行block任務(wù)河泳,dispatch_apply是一個同步調(diào)用,block任務(wù)執(zhí)行n次后才返回,循環(huán)迭代的執(zhí)行順序是不確定的薄霜。
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
//并發(fā)的運(yùn)行一個block任務(wù)5次
dispatch_apply(5, queue, ^(size_t i) {
NSLog(@"do a job %zu times",i+1);
});
NSLog(@"go on");
Dispatch Block
添加到gcd隊列中執(zhí)行的任務(wù)是以block的形式添加的惰瓜,block封裝了需要執(zhí)行功能汉矿,block帶來的開發(fā)效率提升就不說了洲拇,gcd跟block可以說是一對好基友求橄,能夠很好的配合使用。
創(chuàng)建 block
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建block
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"do something");
});
dispatch_async(queue, block);
----
在創(chuàng)建block的時候我們也可以通過設(shè)置QoS蛾绎,指定block對應(yīng)的優(yōu)先級秘通,在dispatch_block_create_with_qos_class中指定QoS類別即可:
----
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
NSLog(@"do something with QoS");
});
dispatch_async(queue, block);
dispatch_block_wait
當(dāng)需要等待前面的任務(wù)執(zhí)行完畢時,我們可以使用dispatch_block_wait這個接口第股,設(shè)置等待時間DISPATCH_TIME_FOREVER會一直等待直到前面的任務(wù)完成:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"before sleep");
[NSThread sleepForTimeInterval:1];
NSLog(@"after sleep");
});
dispatch_async(queue, block);
//等待前面的任務(wù)執(zhí)行完畢
dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
NSLog(@"coutinue");
dispatch_block_notify
dispatch_block_notify當(dāng)觀察的某個block執(zhí)行結(jié)束之后立刻通知提交另一特定的block到指定的queue中執(zhí)行夕吻,該函數(shù)有三個參數(shù)繁仁,第一參數(shù)是需要觀察的block黄虱,第二個參數(shù)是被通知block提交執(zhí)行的queue,第三參數(shù)是當(dāng)需要被通知執(zhí)行的block晤揣,函數(shù)的原型:
void dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue,
dispatch_block_t notification_block);
-----
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t previousBlock = dispatch_block_create(0, ^{
NSLog(@"previousBlock begin");
[NSThread sleepForTimeInterval:1];
NSLog(@"previousBlock done");
});
dispatch_async(queue, previousBlock);
dispatch_block_t notifyBlock = dispatch_block_create(0, ^{
NSLog(@"notifyBlock");
});
//當(dāng)previousBlock執(zhí)行完畢后昧识,提交notifyBlock到global queue中執(zhí)行
dispatch_block_notify(previousBlock, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), notifyBlock);
dispatch_block_cancel
之前在介紹nsopreration的時候提到它的一個優(yōu)點是可以取消某個operation跪楞,現(xiàn)在在iOS8之后侣灶,提交到gcd隊列中的dispatch block也可取消了,只需要簡單的調(diào)用dispatch_block_cancel傳入想要取消的block即可:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block1 = dispatch_block_create(0, ^{
NSLog(@"block1 begin");
[NSThread sleepForTimeInterval:1];
NSLog(@"block1 done");
});
dispatch_block_t block2 = dispatch_block_create(0, ^{
NSLog(@"block2 ");
});
dispatch_async(queue, block1);
dispatch_async(queue, block2);
dispatch_block_cancel(block2);
dispatch_semaphore
在GCD中有三個函數(shù)是semaphore的操作褥影,分別是:
dispatch_semaphore_create 創(chuàng)建一個semaphore
dispatch_semaphore_signal 發(fā)送一個信號
dispatch_semaphore_wait 等待信號
簡單的介紹一下這三個函數(shù)伪阶,第一個函數(shù)有一個整形的參數(shù)处嫌,我們可以理解為信號的總量熏迹,dispatch_semaphore_signal是發(fā)送一個信號,自然會讓信號總量加1坛缕,dispatch_semaphore_wait等待信號赚楚,當(dāng)信號總量少于0的時候就會一直等待,否則就可以正常的執(zhí)行左胞,并讓信號總量-1举户,根據(jù)這樣的原理俭嘁,我們便可以快速的創(chuàng)建一個并發(fā)控制來同步任務(wù)和有限資源訪問控制躺枕。
http://www.cnblogs.com/zhidao-chen/p/3600399.html
http://blog.csdn.net/fhbystudy/article/details/25918451
http://www.w2bc.com/Article/16104
http://www.mamicode.com/info-detail-1218654.html
dispatch_after()
Dispatch Queue的延遲執(zhí)行。
// 延遲 2 秒執(zhí)行:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^{
// code to be executed on the main queue after delay
});
這里要說一下dispatch_time函數(shù)供填,其原型如下:
dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一個參數(shù)一般是DISPATCH_TIME_NOW拐云,表示從現(xiàn)在開始。
第二個參數(shù)就是真正的延時的具體時間捕虽。這里要特別注意的是慨丐,delta參數(shù)是“納秒!”泄私,就是說房揭,延時1秒的話晌端,delta應(yīng)該是“1000000000”=捅暴。=,太長了咧纠,所以理所當(dāng)然系統(tǒng)提供了常量蓬痒,如下:
#define NSEC_PER_SEC 1000000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
關(guān)鍵詞解釋:
NSEC:納秒。
USEC:微妙漆羔。
SEC:秒
PER:每
所以:
NSEC_PER_SEC梧奢,每秒有多少納秒。
USEC_PER_SEC演痒,每秒有多少毫秒亲轨。(注意是指在納秒的基礎(chǔ)上)
NSEC_PER_USEC,每毫秒有多少納秒鸟顺。
[參考]http://www.cocoachina.com/ios/20150505/11751.html
dispatch_once
dispatch\_once是線程安全的惦蚊。它能保證多個線程同時調(diào)用卻只會執(zhí)行塊一次器虾。在dispatch\_once返回之前,所有線程將會等待直到執(zhí)行完畢蹦锋。
dispatch\_once函數(shù)通常用在單例模式上兆沙,它可以保證在程序運(yùn)行期間某段代碼只執(zhí)行一次,如果我們要通過dispatch\_once創(chuàng)建一個單例類莉掂。
+(id)getInstance{
static GapSet *gapSet;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
gapSet = [[GapSet alloc] initObject];
});
return gapSet;
}
注意:dispatch_once_t必須是全局或static變量葛圃。
[參考]
http://blog.afantree.com/gcd/translation-dispatch-once-secret.html
dispatch_suspend和dispatch_resume
http://www.reibang.com/p/85b75c7a6286
dispatch_group
如果想在dispatch\_queue中所有的任務(wù)執(zhí)行完成后在做某種操作,在串行隊列中巫湘,可以把該操作放到最后一個任務(wù)執(zhí)行完成后繼續(xù)装悲,但是在并行隊列中怎么做呢。這就有dispatch\_group 成組操作尚氛。比如
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
// 并行執(zhí)行的線程一
NSLog(@"dispatch-1");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
// 并行執(zhí)行的線程二
NSLog(@"dispatch-2");
});
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
// 匯總結(jié)果
NSLog(@"dispatch-end");
});
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
Dispatch IO
Dispatch Source
http://www.reibang.com/p/f9e01c69a46f
[參考]
http://www.reibang.com/p/f9e01c69a46f
http://blog.devtang.com/blog/2012/02/22/use-gcd/
http://blog.csdn.net/zhangao0086/article/details/38904923
http://www.cocoachina.com/ios/20150505/11751.html
http://www.cnblogs.com/SnailFish/articles/3199863.html
http://www.cnblogs.com/zhidao-chen/category/461198.html
http://blog.afantree.com/ios/the-gcd-learning-directorygcd-debrief.html