GCD簡介
- GCD全稱Grand Central Dispatch,可譯為“牛逼的中樞調(diào)度系統(tǒng)”,是蘋果公司為多核的并行運(yùn)算提供的解決方案。
- 開發(fā)者借助GCD無需直接操作線程,只需要將準(zhǔn)備好的和要執(zhí)行的任務(wù)添加到Dispatch Queue(隊(duì)列)中,GCD會(huì)根據(jù)隊(duì)列類型(串行&并發(fā))和任務(wù)的執(zhí)行類型(同步&異步)來確定要不要開啟子線程、和任務(wù)的執(zhí)行順序。
- 任務(wù)的執(zhí)行順序遵循隊(duì)列的FIFO原則,先進(jìn)先出、后進(jìn)后出。
- 并且要不要開啟線程、開幾條線程以及線程的生命周期(創(chuàng)建線程、調(diào)度熱舞、銷毀線程)都不需要開發(fā)者關(guān)心。
- GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如:雙核、四核、乃至八核),本人也相信蘋果的GCD技術(shù)也是一大伏筆,雖然目前市場上的蘋果手機(jī)大都是雙核,相信在不久的未來蘋果推出更多核芯CPU時(shí)GCD會(huì)自動(dòng)適配,屆時(shí)所有程序員會(huì)發(fā)現(xiàn),蘋果已經(jīng)早先一步有了應(yīng)對(duì)多核硬件的技術(shù),并且也不用擔(dān)心硬件的革新帶來項(xiàng)目重構(gòu)的問題,所以放心地使用GCD!
GCD的幾個(gè)概念
- 任務(wù) 同步&異步:
/**同步執(zhí)行*/
dispatch_sync(dispatch_queue_t _Nonnull queue, ^(void)block)
/**異步步執(zhí)行*/
dispatch_async(dispatch_queue_t _Nonnull queue, ^(void)block)
這里只要執(zhí)行的代碼,為Block代碼塊。既然是Block,是不是煩惱的循環(huán)引用又來了,block只是一個(gè)局部變量,執(zhí)行完畢之后就釋放掉了,不用擔(dān)心循環(huán)引用問題。任務(wù)的執(zhí)行又分同步和異步。
同步:當(dāng)前任務(wù)(代碼)沒有執(zhí)行完畢不會(huì)執(zhí)行下一個(gè)任務(wù);
異步:當(dāng)前任務(wù)沒有執(zhí)行完畢同樣會(huì)執(zhí)行下一個(gè)任務(wù)(只要有任務(wù),GCD就回到線程池中取線程)(主隊(duì)列除外)
- 隊(duì)列 串行&并發(fā):
/**
參數(shù):
1.線程名稱
2.DISPATCH_QUEUE_SERIAL == NULL 串行
DISPATCH_QUEUE_CONCURRENT 并發(fā)
*/
dispatch_queue_create(const char * _Nullable label, dispatch_queue_attr_t _Nullable attr)
主要負(fù)責(zé)調(diào)度任務(wù),所有隊(duì)列都遵循FIFO原則。隊(duì)列又分為串行隊(duì)列和并發(fā)隊(duì)列。
串行 隊(duì)列:一個(gè)一個(gè)地調(diào)度任務(wù)。
并發(fā) 隊(duì)列:可以同時(shí)調(diào)度多個(gè)任務(wù),開不開線程是有任務(wù)決定的。如果是同步任務(wù):當(dāng)一個(gè)任務(wù)沒有執(zhí)行完成,隊(duì)列也會(huì)取任務(wù),只是取出的任務(wù)要等待前一個(gè)任務(wù)執(zhí)行完畢才開始執(zhí)行,不會(huì)開啟線程;如果是異步任務(wù):GCD會(huì)開啟線程同時(shí)執(zhí)行多個(gè)任務(wù)。
- 小結(jié):
開不開線程取決于任務(wù),同步不開線程,異步開線程;
開幾條線程由隊(duì)列決定,串行只開一條,并發(fā)(只有異步條件下)可以開啟多條。
GCD的相關(guān)用法
- 串行隊(duì)列,同步任務(wù)
//串行
dispatch_queue_t q = dispatch_queue_create("chenjinguo", NULL);
for (int i = 0; i < 10; i ++) {
//同步
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
打印
2018-04-03 11:53:46.883277+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 0
2018-04-03 11:53:46.883576+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 1
2018-04-03 11:53:46.883763+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 2
2018-04-03 11:53:46.883928+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 3
2018-04-03 11:53:46.884098+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 4
2018-04-03 11:53:46.884260+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 5
2018-04-03 11:53:46.884390+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 6
2018-04-03 11:53:46.884529+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 7
2018-04-03 11:53:46.884648+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 8
2018-04-03 11:53:46.884789+0800 GCD演示[1099:402251] <NSThread: 0x6040000687c0>{number = 1, name = main} 9
- 不會(huì)開啟線程,順序執(zhí)行
- 串行隊(duì)列,同步任務(wù)
//串行
dispatch_queue_t q = dispatch_queue_create("chen_jinguo",DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i < 10; i ++){
NSLog(@"---------%d-------",i);
//異步
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
打印
2018-04-03 11:58:17.697224+0800 GCD演示[1126:425688] ---------0-------
2018-04-03 11:58:17.697617+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 0
2018-04-03 11:58:17.697436+0800 GCD演示[1126:425688] ---------1-------
2018-04-03 11:58:17.698929+0800 GCD演示[1126:425688] ---------2-------
2018-04-03 11:58:17.699189+0800 GCD演示[1126:425688] ---------3-------
2018-04-03 11:58:17.699223+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 1
2018-04-03 11:58:17.699405+0800 GCD演示[1126:425688] ---------4-------
2018-04-03 11:58:17.699447+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 2
2018-04-03 11:58:17.701129+0800 GCD演示[1126:425688] ---------5-------
2018-04-03 11:58:17.702702+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 3
2018-04-03 11:58:17.703452+0800 GCD演示[1126:425688] ---------6-------
2018-04-03 11:58:17.703831+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 4
2018-04-03 11:58:17.704094+0800 GCD演示[1126:425688] ---------7-------
2018-04-03 11:58:17.704316+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 5
2018-04-03 11:58:17.743690+0800 GCD演示[1126:425688] ---------8-------
2018-04-03 11:58:17.743782+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 6
2018-04-03 11:58:17.743979+0800 GCD演示[1126:425688] ---------9-------
2018-04-03 11:58:17.744001+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 7
2018-04-03 11:58:17.744380+0800 GCD演示[1126:425688] come here
2018-04-03 11:58:17.744407+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 8
2018-04-03 11:58:17.744776+0800 GCD演示[1126:426370] <NSThread: 0x600000274c40>{number = 3, name = (null)} 9
- 會(huì)開啟一條線程,順序執(zhí)行
- 并發(fā)隊(duì)列,異步任務(wù)
//隊(duì)列 - 并發(fā) DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("chenjinguo", DISPATCH_QUEUE_CONCURRENT);
//執(zhí)行 - 異步
for (int i = 0; i < 10; i ++) {
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
打印
2018-04-03 13:36:05.457846+0800 GCD演示[1213:499948] come here
2018-04-03 13:36:05.458007+0800 GCD演示[1213:500170] <NSThread: 0x604000471a80>{number = 4, name = (null)} 1
2018-04-03 13:36:05.458008+0800 GCD演示[1213:500169] <NSThread: 0x60000027ee40>{number = 3, name = (null)} 0
2018-04-03 13:36:05.458010+0800 GCD演示[1213:500171] <NSThread: 0x604000471300>{number = 6, name = (null)} 3
2018-04-03 13:36:05.458012+0800 GCD演示[1213:500180] <NSThread: 0x60000027eb80>{number = 5, name = (null)} 2
2018-04-03 13:36:05.458301+0800 GCD演示[1213:500170] <NSThread: 0x604000471a80>{number = 4, name = (null)} 5
2018-04-03 13:36:05.458310+0800 GCD演示[1213:500168] <NSThread: 0x60000027f200>{number = 7, name = (null)} 4
2018-04-03 13:36:05.458323+0800 GCD演示[1213:500169] <NSThread: 0x60000027ee40>{number = 3, name = (null)} 6
2018-04-03 13:36:05.458345+0800 GCD演示[1213:500171] <NSThread: 0x604000471300>{number = 6, name = (null)} 7
2018-04-03 13:36:05.458451+0800 GCD演示[1213:500168] <NSThread: 0x60000027f200>{number = 7, name = (null)} 8
2018-04-03 13:36:05.458476+0800 GCD演示[1213:500170] <NSThread: 0x604000471a80>{number = 4, name = (null)} 9
- 會(huì)開啟多條線程,非順序執(zhí)行
- 并發(fā)隊(duì)列,同步任務(wù)
//隊(duì)列 - 并發(fā) DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("chenjinguo", DISPATCH_QUEUE_CONCURRENT);
//執(zhí)行 - 同步
for (int i = 0; i < 10; i ++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
打印
2018-04-03 13:37:58.162147+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 0
2018-04-03 13:37:58.162436+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 1
2018-04-03 13:37:58.163300+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 2
2018-04-03 13:37:58.163621+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 3
2018-04-03 13:37:58.163761+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 4
2018-04-03 13:37:58.163916+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 5
2018-04-03 13:37:58.164035+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 6
2018-04-03 13:37:58.164410+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 7
2018-04-03 13:37:58.164839+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 8
2018-04-03 13:37:58.165329+0800 GCD演示[1228:510822] <NSThread: 0x6000000792c0>{number = 1, name = main} 9
2018-04-03 13:37:58.165824+0800 GCD演示[1228:510822] come here
- 不會(huì)開啟線程,順序執(zhí)行
- 同步任務(wù)
在開發(fā)中,通常會(huì)把耗時(shí)任務(wù)放在后臺(tái)執(zhí)行,有時(shí)候,有些任務(wù)彼此有“依賴”關(guān)系!
例子:登錄、支付、下載
利用同步任務(wù),能夠任務(wù)的依賴關(guān)系,前一個(gè)是同步任務(wù),如果不執(zhí)行完,隊(duì)列就不會(huì)調(diào)度后面的任務(wù)
dispatch_queue_t q = dispatch_queue_create("ChenJinguo", DISPATCH_QUEUE_CONCURRENT);
//1、登錄
dispatch_sync(q, ^{
NSLog(@"用戶登錄----- %@",[NSThread currentThread]);
});
//2、支付
dispatch_sync(q, ^{
NSLog(@"用戶支付----- %@",[NSThread currentThread]);
});
for (int i = 0; i < 10; i ++) {
NSLog(@"%d\n",i);
}
//3、下載
dispatch_async(q, ^{
NSLog(@"用戶下載----- %@",[NSThread currentThread]);
});
NSLog(@"come here!");
打印
2018-04-03 13:43:05.538233+0800 GCD演示[1264:539460] 用戶登錄----- <NSThread: 0x604000072580>{number = 1, name = main}
2018-04-03 13:43:05.538480+0800 GCD演示[1264:539460] 用戶支付----- <NSThread: 0x604000072580>{number = 1, name = main}
2018-04-03 13:43:05.538938+0800 GCD演示[1264:539460] 0
2018-04-03 13:43:05.539076+0800 GCD演示[1264:539460] 1
2018-04-03 13:43:05.539220+0800 GCD演示[1264:539460] 2
2018-04-03 13:43:05.539321+0800 GCD演示[1264:539460] 3
2018-04-03 13:43:05.539431+0800 GCD演示[1264:539460] 4
2018-04-03 13:43:05.539679+0800 GCD演示[1264:539460] 5
2018-04-03 13:43:05.540171+0800 GCD演示[1264:539460] 6
2018-04-03 13:43:05.540635+0800 GCD演示[1264:539460] 7
2018-04-03 13:43:05.541084+0800 GCD演示[1264:539460] 8
2018-04-03 13:43:05.541530+0800 GCD演示[1264:539460] 9
2018-04-03 13:43:05.541870+0800 GCD演示[1264:539460] come here!
2018-04-03 13:43:05.541957+0800 GCD演示[1264:540171] 用戶下載----- <NSThread: 0x60400027d080>{number = 3, name = (null)}
- 因?yàn)榈卿浐椭Ц抖际峭饺蝿?wù),在執(zhí)行完同步任務(wù)之后才會(huì)開啟子線程完成其他異步任務(wù)
思考:如果登錄、支付和下載都是耗時(shí)任務(wù),為了增強(qiáng)用戶體驗(yàn),不想將他們放在UI線程中,怎么樣完成這三個(gè)任務(wù)的同步呢?
方法:將三個(gè)任務(wù)作為一個(gè)異步任務(wù),這樣不管放在串行隊(duì)列或者并發(fā)隊(duì)列中,GCD都會(huì)開辟子線程調(diào)度此任務(wù),然后在開啟的子線程上同步調(diào)度三個(gè)任務(wù),就完成了異步中同步調(diào)度任務(wù)
dispatch_queue_t q = dispatch_queue_create("chen", DISPATCH_QUEUE_CONCURRENT);
//包裝三個(gè)任務(wù)為block 異步執(zhí)行
void (^task)() = ^{
dispatch_sync(q, ^{
for (int i = 0; i < 10; i ++) {
NSLog(@"%d--- %@\n",i,[NSThread currentThread]);
}
});
//1、登錄
dispatch_async(q, ^{
NSLog(@"用戶登錄----- %@",[NSThread currentThread]);
});
//2、支付
dispatch_async(q, ^{
NSLog(@"用戶支付----- %@",[NSThread currentThread]);
});
//3、下載
dispatch_async(q, ^{
NSLog(@"用戶下載----- %@",[NSThread currentThread]);
});
};
dispatch_async(q, task);
打印
2018-04-03 13:54:32.628778+0800 GCD演示[1297:579094] 0--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.628785+0800 GCD演示[1297:579087] 用戶登錄----- <NSThread: 0x60000027b940>{number = 3, name = (null)}
2018-04-03 13:54:32.629000+0800 GCD演示[1297:579094] 1--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.629150+0800 GCD演示[1297:579087] 用戶支付----- <NSThread: 0x60000027b940>{number = 3, name = (null)}
2018-04-03 13:54:32.629169+0800 GCD演示[1297:579094] 2--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.629267+0800 GCD演示[1297:579094] 3--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.629269+0800 GCD演示[1297:579087] 用戶下載----- <NSThread: 0x60000027b940>{number = 3, name = (null)}
2018-04-03 13:54:32.629835+0800 GCD演示[1297:579094] 4--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.630147+0800 GCD演示[1297:579094] 5--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.630346+0800 GCD演示[1297:579094] 6--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.630643+0800 GCD演示[1297:579094] 7--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.631249+0800 GCD演示[1297:579094] 8--- <NSThread: 0x604000273700>{number = 4, name = (null)}
2018-04-03 13:54:32.631599+0800 GCD演示[1297:579094] 9--- <NSThread: 0x604000273700>{number = 4, name = (null)}
這樣所有的任務(wù)都會(huì)在子線程中執(zhí)行,并且三個(gè)任務(wù)之間有依賴關(guān)系
- 全局隊(duì)列
GCD提供默認(rèn)的并發(fā)隊(duì)列供全局使用:全局隊(duì)列。其獲取方式和參數(shù)如下:
dispatch_get_global_queue(long identifier, unsigned long flags);
參數(shù)類型為:
long identifier:ios 8.0 告訴隊(duì)列執(zhí)行任務(wù)的“服務(wù)質(zhì)量 quality of service”,系統(tǒng)提供的參數(shù)有:
QOS_CLASS_USER_INTERACTIVE 0x21, 用戶交互(希望盡快完成,用戶對(duì)結(jié)果很期望,不要放太耗時(shí)操作)
QOS_CLASS_USER_INITIATED 0x19, 用戶期望(不要放太耗時(shí)操作)
QOS_CLASS_DEFAULT 0x15, 默認(rèn)(不是給程序員使用的,用來重置對(duì)列使用的)
QOS_CLASS_UTILITY 0x11, 實(shí)用工具(耗時(shí)操作,可以使用這個(gè)選項(xiàng))
QOS_CLASS_BACKGROUND 0x09, 后臺(tái)
QOS_CLASS_UNSPECIFIED 0x00, 未指定
iOS 7.0 之前 優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_HIGH 2 高優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認(rèn)優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺(tái)優(yōu)先級(jí)
BACKGROUND表示用戶不需要知道任務(wù)什么時(shí)候完成,如果選擇這個(gè)選項(xiàng)速度慢得令人發(fā)指,非常不利于調(diào)試!對(duì)于優(yōu)先級(jí)推薦不要搞得太負(fù)責(zé),就用最簡單,以免發(fā)生優(yōu)先級(jí)反轉(zhuǎn)。
unsigned long flags:蘋果官方文檔是這樣解釋的: Flags that are reserved for future use。標(biāo)記是為了未來使用保留的!所以這個(gè)參數(shù)應(yīng)該永遠(yuǎn)指定為0
注意:全局隊(duì)列屬于并發(fā)隊(duì)列,建議在企業(yè)級(jí)應(yīng)用開發(fā)過程中如果不使用全局隊(duì)列,盡量給隊(duì)列起名,這樣有利于錯(cuò)誤跟蹤;另外,在MRC模式下,隊(duì)列需要releasedispatch_release(q);//ARC 情況下不需要release!
- 延時(shí)執(zhí)行
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
/**參數(shù)
1、when 時(shí)間
2、queue
3、Block
*/
//從現(xiàn)在開始執(zhí)行多少納秒之后,讓queue調(diào)度Block的任務(wù)并且異步執(zhí)行
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(when, dispatch_queue_create("jinguo", NULL), ^{
NSLog(@"%@",[NSThread currentThread]);
});
- 執(zhí)行一次
dispatch_once(dispatch_once_t * _Nonnull predicate, ^(void)block) ;
執(zhí)行一次經(jīng)常在單例中用到,是蘋果提供的一次性機(jī)制,不僅能保證代碼只執(zhí)行一次,并且線程安全,線程安全表現(xiàn)在:dispatch_once_t * _Nonnull predicate參數(shù),其原理官方頭文件也有表現(xiàn):
_dispatch_once(dispatch_once_t *predicate,
DISPATCH_NOESCAPE dispatch_block_t block)
{
if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
dispatch_once(predicate, block);
} else {
dispatch_compiler_barrier();
}
DISPATCH_COMPILER_CAN_ASSUME(*predicate == ~0l);
}
其實(shí)具體是什么原理我也看不懂,畢竟C語言認(rèn)識(shí)我我不認(rèn)識(shí)它,其大概意思是我們外面定義的dispatch_once_t參數(shù)在傳入這個(gè)函數(shù)之后,使用它來保證線程安全的,其實(shí)我們做一次打印也可以看出個(gè)大概
for (int i = 0; i < 10; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
dispatch_once(&onceToken, ^{
NSLog(@" %@come here!",[NSThread currentThread]);
});
NSLog(@"來了 ----%d %@",i,[NSThread currentThread]);
});
}
打印
2018-04-03 14:17:22.679810+0800 GCD演示[1372:683676] 0
2018-04-03 14:17:22.679816+0800 GCD演示[1372:683678] 0
2018-04-03 14:17:22.679810+0800 GCD演示[1372:682541] 0
2018-04-03 14:17:22.679866+0800 GCD演示[1372:683679] 0
2018-04-03 14:17:22.680138+0800 GCD演示[1372:683678] <NSThread: 0x60000026f180>{number = 3, name = (null)}come here!
2018-04-03 14:17:22.680170+0800 GCD演示[1372:683680] 5385
2018-04-03 14:17:22.680480+0800 GCD演示[1372:683681] 5385
2018-04-03 14:17:22.680785+0800 GCD演示[1372:683676] 來了 ----1 <NSThread: 0x604000470480>{number = 6, name = (null)}
2018-04-03 14:17:22.680785+0800 GCD演示[1372:682541] 來了 ----0 <NSThread: 0x60000026ec00>{number = 5, name = (null)}
2018-04-03 14:17:22.680795+0800 GCD演示[1372:683678] 來了 ----2 <NSThread: 0x60000026f180>{number = 3, name = (null)}
2018-04-03 14:17:22.680805+0800 GCD演示[1372:683680] 來了 ----4 <NSThread: 0x604000470a40>{number = 4, name = (null)}
2018-04-03 14:17:22.680813+0800 GCD演示[1372:683679] 來了 ----3 <NSThread: 0x604000470800>{number = 7, name = (null)}
2018-04-03 14:17:22.680992+0800 GCD演示[1372:683681] 來了 ----5 <NSThread: 0x600000268680>{number = 8, name = (null)}
2018-04-03 14:17:22.681220+0800 GCD演示[1372:683682] -1
2018-04-03 14:17:22.681490+0800 GCD演示[1372:683683] -1
2018-04-03 14:17:22.681692+0800 GCD演示[1372:683676] -1
2018-04-03 14:17:22.681766+0800 GCD演示[1372:683684] -1
2018-04-03 14:17:22.683852+0800 GCD演示[1372:683682] 來了 ----6 <NSThread: 0x60000026fa80>{number = 9, name = (null)}
2018-04-03 14:17:22.684087+0800 GCD演示[1372:683683] 來了 ----7 <NSThread: 0x60000026f980>{number = 10, name = (null)}
2018-04-03 14:17:22.684468+0800 GCD演示[1372:683676] 來了 ----8 <NSThread: 0x604000470480>{number = 6, name = (null)}
2018-04-03 14:17:22.685031+0800 GCD演示[1372:683684] 來了 ----9 <NSThread: 0x6040004711c0>{number = 11, name = (null)}
onceToken的值一開始是0,執(zhí)行到一次性語句時(shí)變?yōu)?385(或許是其他什么數(shù)),執(zhí)行完畢變?yōu)?1,什么原理還望大神的指點(diǎn)。
還有一種一次性執(zhí)行的方式:互斥鎖 @synchronized(),在單例中用到過,下面將兩種單例創(chuàng)建的方式列舉一下,并比較兩種方式創(chuàng)建單例的效率如何:
@synchronized():
#import "Singleton.h"
static Singleton *_instance;
@implementation Singleton
+ (instancetype)share{
return [[self alloc]init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
@synchronized(self){
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
@end
onceToken:
#import "Singleton2.h"
static Singleton2 *_instance;
@implementation Singleton2
+ (instancetype)share{
return [[self alloc]init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
@end
蘋果推薦使用GCD的一次性,效率高,而互斥鎖效率低,下面比較一下兩種方式的執(zhí)行時(shí)間就一目了然了:
//@synchronized
//獲取代碼開始執(zhí)行時(shí)時(shí)間
CFAbsoluteTime synBegin =CFAbsoluteTimeGetCurrent();
//獲取代碼結(jié)束執(zhí)行時(shí)時(shí)間
Singleton *synSingle = [Singleton share];
CFAbsoluteTime synEnd =CFAbsoluteTimeGetCurrent();
//計(jì)算開始和結(jié)束的時(shí)間差,該時(shí)間差就是循環(huán)創(chuàng)建單例需要的時(shí)間
NSLog(@"synchronized------%f",synBegin- synEnd);
//onceToken
//獲取代碼開始執(zhí)行時(shí)時(shí)間
CFAbsoluteTime onceBegin =CFAbsoluteTimeGetCurrent();
//獲取代碼結(jié)束執(zhí)行時(shí)時(shí)間
Singleton2 *onceSingle = [Singleton share];
CFAbsoluteTime onceEnd =CFAbsoluteTimeGetCurrent();
//計(jì)算開始和結(jié)束的時(shí)間差,該時(shí)間差就是循環(huán)創(chuàng)建單例需要的時(shí)間
NSLog(@"onceToken---------%f",onceBegin- onceEnd);
打印結(jié)果
2018-04-03 15:18:01.046723+0800 GCD演示[1580:939403] synchronized-------0.000021
2018-04-03 15:18:01.046944+0800 GCD演示[1580:939403] onceToken----------0.000004
- 調(diào)度組
dispatch_group_t
GCD頭文件group.h中談到,可以將一組block提交到調(diào)度組(dispatch_group)中,執(zhí)行逐個(gè)串行回調(diào),下面來看看相關(guān)函數(shù)。
dispatch_group_t dispatch_group_create(void);
創(chuàng)建一個(gè)調(diào)度組,釋放調(diào)度組使用dispatch_release()函數(shù),創(chuàng)建成功返回一個(gè)dispatch_group調(diào)度組,失敗則返回NULL.void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
提交一個(gè)閉包函數(shù)(block)到queue中,并關(guān)聯(lián)到指定的group調(diào)度組.通過typedef void (^dispatch_block_t)(void);我們可以發(fā)現(xiàn),該函數(shù)無法給block傳遞參數(shù).
1.group 指定的調(diào)度組,block的關(guān)聯(lián)調(diào)度組。
2.queue 提交閉包函數(shù)(block)的隊(duì)列。
3.block 提交到指定queue的閉包函數(shù)block。void dispatch_group_async_f(dispatch_group_t group,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
提交一個(gè)函數(shù)指針(dispatch_function_t)到queue中,并關(guān)聯(lián)到指定的group調(diào)度組,函數(shù)返回void.
1.group 指定的調(diào)度組,block的關(guān)聯(lián)調(diào)度組。
2.queue 提交閉包函數(shù)(block)的隊(duì)列。
3.context 傳遞到函數(shù)中的的參數(shù)。
4.work 在指定的queue中的指定函數(shù)。long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
執(zhí)行等待,等待所有關(guān)聯(lián)到group調(diào)度組的block執(zhí)行完成,或者等待timeout發(fā)生超時(shí),當(dāng)在超時(shí)時(shí)間timeout內(nèi)執(zhí)行完了所有的block函數(shù),則返回0,否則返回非0值。
1.group 給定調(diào)度組
2.timeout 如果group調(diào)度組里邊的block執(zhí)行時(shí)間非常長,函數(shù)的等待時(shí)間.void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
該函數(shù)指定了一個(gè)block,當(dāng)group調(diào)度組里邊的所有block都執(zhí)行完成時(shí),將通知block關(guān)聯(lián)到group中,并加入到給定的queue隊(duì)列里,當(dāng)group調(diào)度組當(dāng)前沒有任何block關(guān)聯(lián)的時(shí)候?qū)⒘⒓磳lock提交到queue隊(duì)列,并與group調(diào)度組關(guān)聯(lián),該函數(shù)返回void.
1.group 給定的調(diào)度組
2.queue 給定的隊(duì)列.
3.給定的閉包函數(shù).void dispatch_group_notify_f(dispatch_group_t group,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
與disptch_group_notify類似,提交的一個(gè)函數(shù)work作為執(zhí)行體,context是執(zhí)行時(shí)傳遞的參數(shù),該函數(shù)返回void.void dispatch_group_enter(dispatch_group_t group);void dispatch_group_leave(dispatch_group_t group);
這一對(duì)函數(shù)調(diào)用一次意味著非使用dispatch_group_async方式,將一個(gè)block提交到指定的queue上并關(guān)聯(lián)到group調(diào)度組.兩個(gè)函數(shù)必須成對(duì)出現(xiàn)。
在實(shí)際開發(fā)中,需要開啟N個(gè)異步線程,但是后續(xù)操作,需要依賴N個(gè)線程返回的數(shù)據(jù),需要接收所有線程任務(wù)執(zhí)行完成的通知。
//1.對(duì)列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//2.調(diào)度組
dispatch_group_t group = dispatch_group_create();
//3.添加任務(wù),讓隊(duì)列調(diào)度,任務(wù)執(zhí)行情況,最后通知群組
dispatch_group_async(group, q, ^{
NSLog(@"Download A%@",[NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
sleep(1.0);
NSLog(@"Download B%@",[NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
NSLog(@"Download C%@",[NSThread currentThread]);
});
//所有任務(wù)完成之后通知群組
//用調(diào)度組,可以監(jiān)聽全局隊(duì)列的任務(wù),主隊(duì)列去執(zhí)行最后的任務(wù)
//dispatch_group_notify 本身也是異步執(zhí)行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@" OK %@",[NSThread currentThread]);
});
NSLog(@"come here");
注:dispatch_group_notify這個(gè)函數(shù)是異步的,如果要換成同步用dispatch_group_wait(group, DISPATCH_TIME_FOREVER).群組不空,這句代碼一直等,下面代碼不執(zhí)行
// 隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 調(diào)度組
dispatch_group_t group = dispatch_group_create();
// 1. 進(jìn)入群組,給 group 打一個(gè)標(biāo)記,在后續(xù)緊接著的 block 歸 group 監(jiān)聽
// dispatch_group_enter 和 dispatch_group_leave 必須成對(duì)出現(xiàn)!
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:10];
NSLog(@"download A - %@", [NSThread currentThread]);
// 耗時(shí)操作代碼
// 2. 離開群組
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download B - %@", [NSThread currentThread]);
// 耗時(shí)操作代碼
// 2. 離開群組
dispatch_group_leave(group);
});
// 等待群組空,一直到永遠(yuǎn),群組不空,這句代碼就死等,同步
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"OK");
- 主隊(duì)列
dispatch_get_main_queue()
主隊(duì)列是用來在主線程上調(diào)度任務(wù),會(huì)在程序開始時(shí)創(chuàng)建,只需要獲取。
異步任務(wù)
//主隊(duì)列 --> 已啟動(dòng)主線程就可以拿到主隊(duì)列
dispatch_queue_t q = dispatch_get_main_queue();
//異步任務(wù)
dispatch_async(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
for (int i = 0; i < 10 ; i ++) {
NSLog(@"come here");
}
同步任務(wù)會(huì)造成死鎖
//崩潰!?。。。?!
dispatch_sync(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
for (int i = 0; i < 10 ; i ++) {
NSLog(@"come here");
}
解決辦法
//主隊(duì)列同步任務(wù)(不死鎖)
void (^task)() = ^{
dispatch_queue_t q = dispatch_get_main_queue();
//2.異步任務(wù)
dispatch_sync(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
for (int i = 0; i < 10 ; i ++) {
NSLog(@"come here %@",[NSThread currentThread]);
}
};
dispatch_async(dispatch_get_global_queue(0, 0), task);