一鹉究、GCD簡介
??? iOS開發(fā)中多線程的API主要有pthread
自赔,NSThread
匿级,NSOperation
和GCD
津函,前兩者在現(xiàn)在開發(fā)過程中已經(jīng)不常用尔苦,NSOperation
是面向?qū)ο蠓庋b的一套API允坚,而GCD
則是一套純C語言API稠项。
???GCD
(Grand Central Dispatch
)是Apple
開發(fā)的一個(gè)多核編程的解決方法展运,它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)拗胜,它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)锈遥。
二所灸、Dispatch_Queue(隊(duì)列)
1庆寺、隊(duì)列:
????GCD中的隊(duì)列和數(shù)據(jù)結(jié)構(gòu)中的隊(duì)列特性上一致,都是受限制的線性表陵霉,遵循FIFO(First In First Out)
踊挠,即新的任務(wù)需要在隊(duì)尾加入效床,讀取任務(wù)則從隊(duì)首部讀取,即先進(jìn)先出
(1)串行隊(duì)列(Serial Dispatch Queue
)
????串行隊(duì)列就是順序執(zhí)行任務(wù)沪猴,每次執(zhí)行一個(gè)运嗜,上一個(gè)任務(wù)執(zhí)行完畢后執(zhí)行下一個(gè)任務(wù)
(2)并發(fā)隊(duì)列(Concurrent Dispatch Queue
)
????并發(fā)隊(duì)列就是同時(shí)執(zhí)行多個(gè)任務(wù)担租,需要注意的是翩活,這些任務(wù)會按照被添加的順序依次開始執(zhí)行
菠镇。但是任務(wù)完成的順序是任意的
利耍。
2、任務(wù):
??任務(wù):在 GCD 里指的是 Block轴猎,即一段需要執(zhí)行的代碼塊
(1)同步執(zhí)行(dispatch_sync
)
????完成任務(wù)后才會返回捻脖,進(jìn)行下一任務(wù)可婶,可見同步不具備開啟線程能力,只會在當(dāng)前線程依次執(zhí)行
(2)異步執(zhí)行(dispatch_async
)
????完成任務(wù)后立即返回具温,進(jìn)行下一任務(wù),具備多線程能力
注意點(diǎn):
并發(fā)隊(duì)列只會在異步執(zhí)行下生效筐赔,同步執(zhí)行不會觸發(fā)多線程創(chuàng)建铣猩。
三、GCD隊(duì)列編程實(shí)現(xiàn)
1川陆、多線程創(chuàng)建
(1)創(chuàng)建自定義隊(duì)列
/*
dispatch_queue_create 創(chuàng)建一個(gè) dispatch_queue_t 隊(duì)列 剂习,第一個(gè)參數(shù)設(shè)置該隊(duì)列標(biāo)識符,用于調(diào)試使用较沪,第二個(gè)參數(shù)们何,則是隊(duì)列類型
DISPATCH_QUEUE_SERIAL串行隊(duì)列
DISPATCH_QUEUE_CONCURRENT 并發(fā)隊(duì)列
*/
//串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
//并發(fā)隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
(2)系統(tǒng)隊(duì)列:主線程隊(duì)列和全局隊(duì)列
- 主線程隊(duì)列:這個(gè)是主線程的串行隊(duì)列
- 全局隊(duì)列:這是個(gè)全局的并發(fā)隊(duì)列鹦蠕,很多時(shí)候可以不需要自己創(chuàng)建并發(fā)隊(duì)列,直接獲取全局隊(duì)列即可 第一個(gè)參數(shù)為優(yōu)先級,這是個(gè)大概優(yōu)先級設(shè)置
/**
DISPATCH_QUEUE_PRIORITY_HIGH //高優(yōu)先級
DISPATCH_QUEUE_PRIORITY_DEFAULT //默認(rèn)優(yōu)先級
DISPATCH_QUEUE_PRIORITY_LOW //低優(yōu)先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND //后臺優(yōu)先級
*/
//主線程隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//全局隊(duì)列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
(3)隊(duì)列中添加任務(wù)執(zhí)行
//異步執(zhí)行
dispatch_async(serialQueue, ^{
NSLog(@"serialQueue中異步執(zhí)行");
});
//同步執(zhí)行
dispatch_sync(serialQueue, ^{
NSLog(@"serialQueue中同步執(zhí)行");
});
(4)總共為3種隊(duì)列類型噪伊,6種組合方式2拙寡、3種類型6種組合方式介紹
(1)主線程同步執(zhí)行(死鎖)
代碼:
NSLog(@"Begin");
//主線程同步同步執(zhí)行
dispatch_sync(mainQueue, ^{
NSLog(@"主線程同步同步執(zhí)行");
});
NSLog(@"End");
運(yùn)行結(jié)果:
2017-05-03 16:50:31.542 GCDDemo[41489:596607] Begin
??只打印了begin
诚啃,并沒有執(zhí)行下去,事實(shí)上這里發(fā)生了死鎖。首先代碼是在主線程執(zhí)行,主線程是串行隊(duì)列,而dispatch_sync
是同步執(zhí)行尚洽,sync
添加的任務(wù)需要執(zhí)行需要等待NSLog(@"End");
執(zhí)行完畢,NSLog(@"End");
任務(wù)本身也添加在主線程隊(duì)列中,所以執(zhí)行這個(gè)任務(wù)的前提是sync
添加的任務(wù)執(zhí)行完畢,這就出現(xiàn)了兩個(gè)任務(wù)互相等待,造成死鎖
??總結(jié):主線程中執(zhí)行同步任務(wù)會發(fā)生死鎖,即:串行隊(duì)列中嵌套串行隊(duì)列任務(wù)會發(fā)生死鎖
(2)主線程異步執(zhí)行
代碼:
NSLog(@"Begin");
//主線程異步執(zhí)行
dispatch_async(mainQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------主線程異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_async(mainQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------主線程異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_async(mainQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------主線程異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
NSLog(@"End");
運(yùn)行結(jié)果:
2017-05-03 17:09:26.031 GCDDemo[42671:613068] Begin
2017-05-03 17:09:26.031 GCDDemo[42671:613068] End
2017-05-03 17:09:26.042 GCDDemo[42671:613068] 0---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.043 GCDDemo[42671:613068] 1---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.044 GCDDemo[42671:613068] 2---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.052 GCDDemo[42671:613068] 0---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.057 GCDDemo[42671:613068] 1---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.059 GCDDemo[42671:613068] 2---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.060 GCDDemo[42671:613068] 0---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.062 GCDDemo[42671:613068] 1---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.063 GCDDemo[42671:613068] 2---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
總結(jié):主線程隊(duì)列異步執(zhí)行不會開辟線程,會在當(dāng)前線程同步執(zhí)行。
(3)串行隊(duì)列同步執(zhí)行
代碼:
NSLog(@"Begin");
//串行隊(duì)列同步執(zhí)行
dispatch_sync(serialQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------串行隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_sync(serialQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------串行隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_sync(serialQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------串行隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]);
}
});
NSLog(@"End");
運(yùn)行結(jié)果:
2017-05-03 17:25:48.225 GCDDemo[43694:625941] Begin
2017-05-03 17:25:48.225 GCDDemo[43694:625941] 0---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 2---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 0---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 2---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 0---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 2---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] End
??總結(jié): 串行隊(duì)列異步執(zhí)行不會開辟多線程,只會在一條線程中依次執(zhí)行
(4)串行隊(duì)列異步執(zhí)行
代碼:
NSLog(@"Begin");
//串行隊(duì)列異步執(zhí)行
dispatch_async(serialQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------串行隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_async(serialQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------串行隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_async(serialQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------串行隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
NSLog(@"End");
運(yùn)行結(jié)果:
2017-05-03 17:20:16.514 GCDDemo[43349:621375] Begin
2017-05-03 17:20:16.514 GCDDemo[43349:621375] End
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 0---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 2---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 1---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 2---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 1---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 2---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
??總結(jié): 串行隊(duì)列異步執(zhí)行不會開辟多線程摇肌,只會在一條線程中依次執(zhí)行
??串行隊(duì)列同步和異步執(zhí)行都沒有開辟多線程
肯适,在一條線程中同步執(zhí)行赎婚,那么對于串行隊(duì)列同步和異步執(zhí)行有什么區(qū)別呢?
??區(qū)別只有一點(diǎn):
??dispatch_async
:不會阻塞當(dāng)前隊(duì)列,立即返回添加當(dāng)前隊(duì)列后面任務(wù)绢馍,可以看到上圖打印結(jié)果,先打印end
搁痛。再打印async
任務(wù)
??dispatch_sync
:會阻塞當(dāng)前隊(duì)列,等該sync
任務(wù)全部執(zhí)行完畢之后再添加當(dāng)前隊(duì)列后面任務(wù)抽碌,可以看到上圖打印結(jié)果痴颊,先打印完sync
任務(wù)打印end
抛丽。
(6)并發(fā)隊(duì)列同步執(zhí)行
NSLog(@"Begin");
//串行隊(duì)列同步執(zhí)行
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------并發(fā)隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------并發(fā)隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------并發(fā)隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]);
}
});
NSLog(@"End");
運(yùn)行結(jié)果:
2017-05-03 17:34:24.407 GCDDemo[44222:633000] Begin
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 0---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 1---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.409 GCDDemo[44222:633000] 2---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.410 GCDDemo[44222:633000] End
總結(jié): 并發(fā)隊(duì)列同步執(zhí)行不會開辟多線程,只會在一條線程中依次執(zhí)行
(6)并發(fā)隊(duì)列異步執(zhí)行
NSLog(@"Begin");
//并發(fā)隊(duì)列異步執(zhí)行
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------并發(fā)隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------并發(fā)隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i ++) {
NSLog(@"%d---------并發(fā)隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]);
}
});
NSLog(@"End");
運(yùn)行結(jié)果:
2017-05-03 17:38:10.893 GCDDemo[44461:636216] Begin
2017-05-03 17:38:10.894 GCDDemo[44461:636216] End
2017-05-03 17:38:10.894 GCDDemo[44461:636252] 0---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636254] 0---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636251] 0---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.897 GCDDemo[44461:636252] 1---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.899 GCDDemo[44461:636254] 1---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.900 GCDDemo[44461:636251] 1---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.903 GCDDemo[44461:636252] 2---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.904 GCDDemo[44461:636254] 2---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.905 GCDDemo[44461:636251] 2---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a800>{number = 3, name = (null)}
??總結(jié): 并發(fā)隊(duì)列異步執(zhí)行會開辟多線程執(zhí)行,并且執(zhí)行順序不定
四惕鼓、應(yīng)用示例
異步處理數(shù)據(jù)完成后汞幢,主線程更新UI界面
dispatch_async(globalQueue, ^{
//異步數(shù)據(jù)處理...
dispatch_async(mainQueue, ^{
//主線程更新UI
});
});
??可以看到運(yùn)用GCD可以輕松的進(jìn)行線程間通信,使用指定隊(duì)列進(jìn)行串行處理任務(wù),例如數(shù)據(jù)庫存儲等依賴線程安全等處理
//串行隊(duì)列保證線程安全
dispatch_sync(serialQueue, ^{
//數(shù)據(jù)存儲等依賴線程安全操作...
});
??FMDBDataBaseQueue就是使用的串行隊(duì)列來保證線程安全的
五似嗤、其他GCD API
1每聪、dispatch_after
dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"After");
});
??延遲執(zhí)行系谐,有時(shí)候可以作為定時(shí)執(zhí)行作用,需要注意的是嗦董,該函數(shù)并不是在指定時(shí)間后執(zhí)行處理,而只是在指定時(shí)間追加處理到dispatch_queue
,實(shí)際執(zhí)行時(shí)間受到runloop
的狀態(tài)影響宪卿,存在偏差芭碍。
2杉女、dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[SchoolManager alloc] init];
});
??執(zhí)行一次,在block中的代碼全局只會執(zhí)行一次猴蹂,被廣泛用于單例創(chuàng)建中
3撮躁、dispatch_suspend / dispatch_resume
dispatch_suspend(queque); //掛起隊(duì)列
dispatch_resume(queue);//恢復(fù)隊(duì)列
??掛起對已經(jīng)執(zhí)行的任務(wù)沒有影響,會暫停所有未執(zhí)行的任務(wù)以及后續(xù)追加的任務(wù)浪慌,恢復(fù)則會繼續(xù)執(zhí)行所有被掛起的任務(wù)
4槽唾、dispatch_set_target_queue
//搬運(yùn)一段代碼
dispatch_queue_t mySerialDispatchQueue =
dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);
??dispatch_set_target_queue
主要有兩個(gè)作用:
??(1)設(shè)置優(yōu)先級:自建的隊(duì)列優(yōu)先級默認(rèn)和系統(tǒng)隊(duì)列優(yōu)先級一致齐佳,設(shè)置參數(shù)1隊(duì)列的優(yōu)先級和參數(shù)2的優(yōu)先級一致私恬,顯然你不能設(shè)置系統(tǒng)全局隊(duì)列和主隊(duì)列優(yōu)先級
??(2)更改隊(duì)列的執(zhí)行層級:如果多個(gè)串行隊(duì)列設(shè)置函數(shù)目標(biāo)串行隊(duì)列是某一個(gè)串行隊(duì)列,原本并發(fā)執(zhí)行的串行隊(duì)列炼吴,在目標(biāo)串行隊(duì)列上只能依次執(zhí)行践付,代碼示例如下
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"1 in");
[NSThread sleepForTimeInterval:3.f];
NSLog(@"1 out");
});
dispatch_async(queue2, ^{
NSLog(@"2 in");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"2 out");
});
dispatch_async(queue3, ^{
NSLog(@"3 in");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 out");
});
5、dispatch_group
??主要應(yīng)對這樣的需求缺厉,異步處理完A和B任務(wù),兩者都執(zhí)行完執(zhí)行C任務(wù)隧土,和NSOperation中的依賴一致提针。示例如下
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//異步耗時(shí)操作A
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//異步耗時(shí)操作B
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 前面的異步操作A和B都執(zhí)行完之后,回主線程
});
6曹傀、dispatch_apply
??這是dispatch_sync
和dispatch_group
的關(guān)聯(lián)API辐脖,按指定次數(shù)將指定的Block追加到指定的Dispatch_Queue中,并且等待全部執(zhí)行結(jié)束皆愉∈燃郏可以用于遍歷效果
//全局隊(duì)列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, globalQueue, ^(size_t index) {
//重復(fù)執(zhí)行10次
NSLog(@"%zu",index);
});
//10次執(zhí)行完之后艇抠,再執(zhí)行Done
NSLog(@"Done");
6、dispatch_barrier_sync / dispatch_barrier_async
??柵欄:有時(shí)候創(chuàng)建兩組并發(fā)任務(wù)久锥,如果在中間加入柵欄家淤,那么這個(gè)任務(wù)會在第一組任務(wù)完成后執(zhí)行,并且第二組任務(wù)會在柵欄任務(wù)完成后才開始執(zhí)行瑟由,如下圖所示在并發(fā)隊(duì)列中添加任務(wù)絮重,執(zhí)行順序一定是:任務(wù)組A -> Barrier任務(wù) -> 任務(wù)組B,
示例代碼如下:
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
NSLog(@"1---------");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2--------");
});
dispatch_barrier_async(concurrentQueue, ^{
NSLog(@"barrier--------");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"3--------");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"4--------");
});
??打印執(zhí)行順序1歹苦,2不定青伤,3,4也不定殴瘦,但是barrier一定在1和2之后狠角,3和4一定在barrier之后,可以自行添加數(shù)量測試蚪腋。
??應(yīng)用場景丰歌,經(jīng)常我們會自行創(chuàng)建一個(gè)隊(duì)列進(jìn)行文件讀取和存儲,一般文件讀取的速度很快辣吃,可以使用并發(fā)隊(duì)列多線程提高讀取效率动遭,但是文件存儲需要考慮到線程安全,那么我們就可以使用barrier進(jìn)行文件存儲操作神得,類似這樣
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
//文件讀取
});
dispatch_async(concurrentQueue, ^{
//文件讀取
});
dispatch_barrier_async(concurrentQueue, ^{
//文件存儲
});
dispatch_barrier_async(concurrentQueue, ^{
//文件存儲
});
dispatch_async(concurrentQueue, ^{
//文件讀取
});
??可見使用barrier
可以輕松高效的實(shí)現(xiàn)文件IO厘惦。
??dispatch_barrier
需要注意的點(diǎn),dispatch_barrier
只會對自建的隊(duì)列生效哩簿,對于系統(tǒng)的mainQueue
和GlobalQueue
不起作用
??dispatch_barrier_async
和dispatch_barrier_sync
的區(qū)別也同樣在于同步和異步宵蕉,dispatch_barrier_async
不會等待自己任務(wù)執(zhí)行完畢才會在隊(duì)列中添加其他任務(wù),而dispatch_barrier_sync
會等待自己任務(wù)執(zhí)行完畢后才會在隊(duì)列中添加其他任務(wù)节榜。
??AFNetworking
中大量使用dispatch_barrier_async
做數(shù)據(jù)存儲羡玛,可以看到dispatch_barrier_async
也可以實(shí)現(xiàn)串行同步隊(duì)列效果,相比于dispatch_sync
容易產(chǎn)生死鎖(在串行隊(duì)列中同步添加該串行隊(duì)列任務(wù)即會發(fā)生死鎖)宗苍,dispatch_barrier_async
更加安全稼稿。
五、Dispatch Semaphore 信號量
??dispatch_semaphore_t
信號量本質(zhì)上是一種鎖讳窟。
??下面我們看下信號量的使用:dispatch_semaphore_t
的作用之一解決資源搶奪問題
??對于數(shù)據(jù)存儲類似數(shù)據(jù)庫让歼,非原子性可變字典和可變數(shù)組等多線程下不安全的操作,可以使用同步隊(duì)列保證線程安全丽啡,那么在并發(fā)隊(duì)列中谋右,可以使用信號量來解決資源搶奪問題
//全局隊(duì)列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建一個(gè)信號量,初始值為1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1) ; //創(chuàng)建可變數(shù)組
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i< 1000; ++i) {
dispatch_async(queue, ^{
//這里會一直等待补箍,直到信號量大于等于1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) ;
//執(zhí)行到這里改执,消費(fèi)一個(gè)信號量
NSLog(@"%@",[NSThread currentThread]);
[array addObject:[NSNumber numberWithInt:i]];
//這里增加一個(gè)信號量
dispatch_semaphore_signal(semaphore);
});
}
代碼解讀:
??dispatch_semaphore_create(1)
創(chuàng)建了值為1信號量
??dispatch_semaphore_wait
啸蜜,如果信號量的值大于等于1,那么辈挂,信號量值減1衬横,然后向下執(zhí)行,如果信號量值為0呢岗,一直等待冕香。直到大于等于1的時(shí)候,率先進(jìn)入等待狀態(tài)的異步隊(duì)列率先執(zhí)行
??dispatch_semaphore_signal
信號量值加1
??實(shí)際這種效果和加鎖的本質(zhì)一致后豫,dispatch_semaphore_t
的另外一個(gè)作用就是可以控制線程并發(fā)數(shù)量悉尾,iOS7之后系統(tǒng)自動(dòng)開辟的線程數(shù)量可以多達(dá)60-70,而GCD中并沒有提供控制線程數(shù)量的API挫酿,NSOperation
中可以設(shè)置最大線程數(shù)构眯。
??下面我們使用信號量來實(shí)現(xiàn)一下線程數(shù)量控制:
//線程并發(fā)數(shù)限制
static dispatch_semaphore_t limitSemaphore;
//控制專用隊(duì)列
static dispatch_queue_t serialQueue;
//單例創(chuàng)建
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//設(shè)置最大線程并發(fā)數(shù)為5
limitCount = dispatch_semaphore_create(5);
serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
});
dispatch_async(serialQueue, ^{
//信號量>=1繼續(xù)執(zhí)行,否則等待
dispatch_semaphore_wait(limitSemaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
//這里執(zhí)行一些任務(wù)
NSLog(@"%@",[NSThread currentThread]);
//在該工作線程執(zhí)行完成后釋放信號量
dispatch_semaphore_signal(limitSemaphore);
});
});
六早龟、dispatch source
??dispatch source
是一組不常用的GCD API
惫霸。是BSD系內(nèi)核慣有功能kqueue的包裝。簡單來說葱弟,dispatch source
是一個(gè)監(jiān)視某些類型事件的對象板惑。它支持所有kqueue
所支持的事件以及mach(mach介紹可以看這里mach wikipedia)端口滥沫、內(nèi)建計(jì)時(shí)器支持和用戶事件,CPU負(fù)荷占用小,資源占用小翔横。
??dispatch sourc聯(lián)結(jié)流程:在任一線程上調(diào)用dispatch_source_merge_data 這個(gè)函數(shù)后畔勤,會執(zhí)行 Dispatch Source 事先定義好的句柄(可以簡單理解句柄就是block )(是不是有點(diǎn)通知懂盐,回調(diào)的味道哈)
??代碼展示:
//全局隊(duì)列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建source
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
//定義source的句柄
dispatch_source_set_event_handler(source, ^{
//調(diào)用一次dispatch_source_merge_data會調(diào)用這個(gè)句柄
NSLog(@"%lu",dispatch_source_get_data(source));
});
//默認(rèn)source是suspend的北启,需要resume生效 dispatch_resume(source);
//遍歷10次
dispatch_apply(10, globalQueue, ^(size_t index) {
// merge data dispatch_source_merge_data(source, 1);
});
??這段程序簡單邏輯:調(diào)用dispatch_source_merge_data 會觸發(fā)實(shí)現(xiàn)定義好的事件
dispatch_source_set_event_handler(source, ^{ //調(diào)用一次dispatch_source_merge_data會調(diào)用這個(gè)句柄 NSLog(@"%lu",dispatch_source_get_data(source)); });
dispatch_source_create 函數(shù)參數(shù)DISPATCH_SOURCE_TYPE_DATA_ADD 累加
??當(dāng)注冊系統(tǒng)事件的時(shí)候,有時(shí)候系統(tǒng)還沒來得及通知應(yīng)用程序蝌麸,這個(gè)時(shí)候点寥,系統(tǒng)會累計(jì)傳遞過來的值
DISPATCH_SOURCE_TYPE_DATA_OR
邏輯或處理累計(jì)傳遞過來的值
其他:
DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口發(fā)送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
DISPATCH_SOURCE_TYPE_PROC 監(jiān)測進(jìn)程相關(guān)事件
DISPATCH_SOURCE_TYPE_READ 可讀取文件映像
DISPATCH_SOURCE_TYPE_SIGNAL 接收信號
DISPATCH_SOURCE_TYPE_TIMER 定時(shí)器
DISPATCH_SOURCE_TYPE_VNODE 文件系統(tǒng)變更
DISPATCH_SOURCE_TYPE_WRITE 可寫入文件映像
參考博客:http://www.reibang.com/p/8920a3da98b8