【作者前言】:13年入圈眠砾,分享些本人工作中遇到的點(diǎn)點(diǎn)滴滴那些事兒虏劲,17年剛開(kāi)始寫(xiě)博客,高手勿噴褒颈!以分享交流為主柒巫,歡迎各路豪杰點(diǎn)評(píng)改進(jìn)!
1.應(yīng)用場(chǎng)景:
多線程神器---GCD哈肖,很多開(kāi)發(fā)的場(chǎng)景中吻育,我們都會(huì)使用到。
2.實(shí)現(xiàn)目標(biāo):
針對(duì)面試中可能遇到的問(wèn)題淤井,進(jìn)行反向的剖析知識(shí)點(diǎn)布疼。
3.代碼說(shuō)明:【比喻~隊(duì)列與函數(shù)存在一種關(guān)系:C4-2的組合關(guān)系】
1) 知識(shí)點(diǎn)預(yù)熱:首先需要了解同步&異步、串行&并行的關(guān)系
同步執(zhí)行:
dispatch_sync()币狠,這個(gè)函數(shù)會(huì)把一個(gè)block加入到指定的隊(duì)列中游两,而且會(huì)一直等到執(zhí)行完blcok,這個(gè)函數(shù)才返回漩绵。因此在block執(zhí)行完之前贱案,調(diào)用dispatch_sync方法的線程是阻塞的。 !>!>!即需要等待執(zhí)行結(jié)果后再執(zhí)行其他任務(wù)
異步執(zhí)行
dispatch_async()止吐,這個(gè)函數(shù)也會(huì)把一個(gè)block加入到指定的隊(duì)列中宝踪,但是和同步執(zhí)行不同的是,這個(gè)函數(shù)把block加入隊(duì)列后不等block的執(zhí)行就立刻返回了碍扔。 !>!>!即 無(wú)需等待執(zhí)行結(jié)果瘩燥,繼續(xù)執(zhí)行其他任務(wù)
小結(jié):
dispatch_async() 和 dispatch_sync() 他們的作用是將 任務(wù)(block)添加進(jìn)指定的隊(duì)列中。并根據(jù)是否為sync決定調(diào)用該函數(shù)的線程是否需要阻塞不同。
!!!:這里調(diào)用該函數(shù)的線程并不執(zhí)行 參數(shù)中指定的任務(wù)(block塊)厉膀,任務(wù)的執(zhí)行者是GCD分配給任務(wù)所在隊(duì)列的線程囤屹。
即 ---> 調(diào)用dispatch_sync() 和 dispatch_async() 的線程醋界,并不一定是任務(wù)(block塊)的真正執(zhí)行者
串行隊(duì)列
如dispatch_get_main_queue() 該隊(duì)列中所有任務(wù)都是串行依次執(zhí)行,可以保證在執(zhí)行某個(gè)任務(wù)時(shí)杈绸,在它前面進(jìn)入隊(duì)列的所有任務(wù)肯定執(zhí)行完了百新。對(duì)于每一個(gè)不同的串行隊(duì)列企软,系統(tǒng)會(huì)為這個(gè)隊(duì)列建立唯一的線程來(lái)執(zhí)行代碼
并行隊(duì)列
如dispatch_get_global_queue() 該隊(duì)列中的任務(wù)它們的執(zhí)行結(jié)束時(shí)間是不確定的,取決于每個(gè)任務(wù)的耗時(shí)饭望。并發(fā)隊(duì)列中的任務(wù):GCD會(huì)動(dòng)態(tài)分配多條線程來(lái)執(zhí)行澜倦。具體幾條線程取決于當(dāng)前內(nèi)存使用狀況聚蝶,線程池中線程數(shù)等因素
2) 代碼實(shí)操+詳細(xì)解讀分析
- 【主隊(duì)列---串行同步】
/** 主隊(duì)列---串行同步 --測(cè)試
不會(huì)開(kāi)啟線程 等待執(zhí)行
*/
- (void)mainSyncTest {
NSLog(@"0");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1");
});
NSLog(@"2");
}
/****************控制臺(tái)輸出結(jié)果*******************/
0 --- >崩潰Thread 1: EXC_BAD_INSTRUCTION 造成死鎖
/****************結(jié)果分析******************/
打印 0 ---> 正常輸出后,
當(dāng)前執(zhí)行這個(gè)dispatch_get_main_queue()隊(duì)列的是主線程藻治。
執(zhí)行了dispatch_sync()函數(shù)后,將block(打印1) 添加到了main_queue中巷挥,
同時(shí)調(diào)用dispatch_syn這個(gè)函數(shù)的線程(也就是主線程)會(huì)被阻塞桩卵,需要等待
block(打印1)執(zhí)行完成,而此時(shí)執(zhí)行主線程隊(duì)列任務(wù)的線程正是主線程倍宾,
此時(shí)他處于阻塞狀態(tài)雏节,所以block(打印1)永遠(yuǎn)不會(huì)被執(zhí)行,因此主線程一直處于阻塞狀態(tài)高职。
因此這段代碼運(yùn)行后钩乍,并非卡在block(打印1)中無(wú)法返回,而是根本無(wú)法執(zhí)行到這個(gè)block(打印1)怔锌!
導(dǎo)致你等我我等你寥粹,形成死鎖。無(wú)法打印 1 和 2
- 【主隊(duì)列---串行異步】
/** 主隊(duì)列---串行異步 --測(cè)試
不會(huì)開(kāi)啟線程埃元, 順序執(zhí)行
*/
- (void)mainAysncTest {
NSLog(@"0");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"1");
});
NSLog(@"2");
}
/****************控制臺(tái)輸出結(jié)果******************/
0 2 1
/****************結(jié)果分析******************/
dispatch_get_main_queue()屬于串行隊(duì)列涝涤,順序執(zhí)行
dispatch_async()異步調(diào)用,不需要等待直接輸出執(zhí)行結(jié)果岛杀,
即 0 ---> 2 ---> 1
- 【全局隊(duì)列---并行同步】
/** 全局隊(duì)列---并行同步 --測(cè)試
不會(huì)開(kāi)辟線程阔拳!
*/
- (void)globalSyncTest {
for (int i = 0; i < 5; i ++) {
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%d -- %@",i,[NSThread currentThread]);
});
}
for (long i = 0; i < 100000; i ++) {
//相當(dāng)于做個(gè)一定的延時(shí)處理...方便查看效果
}
NSLog(@"hello sync Global_Queue");
}
/****************控制臺(tái)輸出結(jié)果******************/
0 -- <NSThread: 0x6000003faa40>{number = 1, name = main}
1 -- <NSThread: 0x6000003faa40>{number = 1, name = main}
2 -- <NSThread: 0x6000003faa40>{number = 1, name = main}
3 -- <NSThread: 0x6000003faa40>{number = 1, name = main}
4 -- <NSThread: 0x6000003faa40>{number = 1, name = main}
hello sync Global_Queue
/****************結(jié)果分析******************/
dispatch_get_global_queue() 屬于并行隊(duì)列,結(jié)果無(wú)序
dispatch_sync() 同步調(diào)用类嗤,需要等待執(zhí)行結(jié)束后在執(zhí)行其他任務(wù)糊肠,
即 阻塞當(dāng)前線程,順序執(zhí)行遗锣,故并行也無(wú)法發(fā)揮作用货裹,需要一個(gè)等一個(gè)依次執(zhí)行,
所以這里并沒(méi)有開(kāi)辟新的線程黄伊!
- 【全局隊(duì)列---并行異步】
/** 全局隊(duì)列---并行異步 --測(cè)試
*/
- (void)globalAsyncTest {
for (int i = 0; i < 5; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%d -- %@",i,[NSThread currentThread]);
});
}
for (long i = 0; i < 100000; i ++) {
//相當(dāng)于做個(gè)一定的延時(shí)處理...方便查看效果
}
NSLog(@"hello async Global_Queue");
}
/****************控制臺(tái)輸出結(jié)果******************/
0 -- <NSThread: 0x6000032ae000>{number = 3, name = (null)}
2 -- <NSThread: 0x6000032ae0c0>{number = 6, name = (null)}
1 -- <NSThread: 0x6000032adfc0>{number = 4, name = (null)}
3 -- <NSThread: 0x600003292940>{number = 5, name = (null)}
4 -- <NSThread: 0x6000032ae000>{number = 3, name = (null)}
hello async Global_Queue
/****************結(jié)果分析******************/
dispatch_get_global_queue() 屬于并行隊(duì)列泪酱,執(zhí)行結(jié)果時(shí)序不一定
dispatch_async()異步調(diào)用,不需要等待直接順序執(zhí)行还最,
所以墓阀,線程并非只有一個(gè),開(kāi)辟了多個(gè)線程并行處理任務(wù)拓轻,輸出的結(jié)果的順序也不確定
- 【通用的串行同步】
/** 串行同步隊(duì)列 --測(cè)試
不開(kāi)辟線程 順序執(zhí)行
*/
- (void)serialSyncTest {
dispatch_queue_t serialQueue = dispatch_queue_create("ypSerialQueue", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 5; i ++) {
dispatch_sync(serialQueue, ^{
NSLog(@"%d -- %@",i, [NSThread currentThread]);
});
}
NSLog(@"hello sync serialQueue");
}
/****************控制臺(tái)輸出結(jié)果******************
0 -- <NSThread: 0x600000365200>{number = 1, name = main}
1 -- <NSThread: 0x600000365200>{number = 1, name = main}
2 -- <NSThread: 0x600000365200>{number = 1, name = main}
3 -- <NSThread: 0x600000365200>{number = 1, name = main}
4 -- <NSThread: 0x600000365200>{number = 1, name = main}
hello sync serialQueue
*/
/****************結(jié)果分析******************
serialQueue 屬于串行隊(duì)列 不會(huì)開(kāi)辟線程 順序執(zhí)行
dispatch_sync()同步調(diào)用斯撮,需要等待 順序執(zhí)行,
串行隊(duì)列情況下扶叉,只能依次執(zhí)行勿锅,同步調(diào)用 等待出結(jié)果帕膜,故輸出如上
*/
- 【通用的串行異步】
/** 串行異步隊(duì)列 --測(cè)試
不開(kāi)辟線程 順序執(zhí)行
*/
- (void)serialAsyncTest {
dispatch_queue_t serialQueue = dispatch_queue_create("ypSerialQueue", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 5; i ++) {
dispatch_async(serialQueue, ^{
NSLog(@"%d -- %@",i, [NSThread currentThread]);
});
}
NSLog(@"hello serial Queue");
}
/****************控制臺(tái)輸出結(jié)果******************
hello serial Queue
0 -- <NSThread: 0x600003904d40>{number = 3, name = (null)}
1 -- <NSThread: 0x600003904d40>{number = 3, name = (null)}
2 -- <NSThread: 0x600003904d40>{number = 3, name = (null)}
3 -- <NSThread: 0x600003904d40>{number = 3, name = (null)}
4 -- <NSThread: 0x600003904d40>{number = 3, name = (null)}
*/
/****************結(jié)果分析******************
serialQueue 屬于串行隊(duì)列 不會(huì)開(kāi)辟線程 順序執(zhí)行
dispatch_async()異步調(diào)用,不需要等待直接順序執(zhí)行溢十,
串行隊(duì)列情況下垮刹,只能依次執(zhí)行,異步調(diào)用直接出結(jié)果张弛,故輸出如上
*/
- 【通用的并行同步】
/** 并行同步 ---測(cè)試
不會(huì)創(chuàng)建線程 順序執(zhí)行
*/
- (void)concurrentSyncTest {
//創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("ypConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 5; i ++) {
dispatch_sync(concurrentQueue, ^{
NSLog(@"%d -- %@",i, [NSThread currentThread]);
});
}
NSLog(@"hello sync concurrent Queue");
}
/****************控制臺(tái)輸出結(jié)果******************
0 -- <NSThread: 0x6000020ee940>{number = 1, name = main}
1 -- <NSThread: 0x6000020ee940>{number = 1, name = main}
2 -- <NSThread: 0x6000020ee940>{number = 1, name = main}
3 -- <NSThread: 0x6000020ee940>{number = 1, name = main}
4 -- <NSThread: 0x6000020ee940>{number = 1, name = main}
hello sync concurrent Queue
*/
/****************結(jié)果分析******************
concurrentQueue 屬于并行隊(duì)列荒典,執(zhí)行結(jié)果時(shí)序不一定
dispatch_sync()同步調(diào)用,需要等待 順序執(zhí)行吞鸭,
只要同步 就等待執(zhí)行寺董, 所以沒(méi)有開(kāi)辟線程 結(jié)果如上
*/
- 【通用的并行異步】
/** 并行異步:*/
- (void)concurrentAsyncTest {
//創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("ypconcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 5; i ++) {
dispatch_async(concurrentQueue, ^{
NSLog(@"%d -- %@",i, [NSThread currentThread]);
});
}
for (long i = 0; i < 100000; i ++) {
//相當(dāng)于做個(gè)一定的延時(shí)處理...方便查看效果
}
NSLog(@"hello concurrent Queue");
}
/****************控制臺(tái)輸出結(jié)果******************/
0 -- <NSThread: 0x600001633600>{number = 3, name = (null)}
3 -- <NSThread: 0x600001633580>{number = 5, name = (null)}
2 -- <NSThread: 0x60000160d380>{number = 4, name = (null)}
1 -- <NSThread: 0x600001633500>{number = 6, name = (null)}
4 -- <NSThread: 0x600001633580>{number = 5, name = (null)}
hello concurrent Queue
/****************結(jié)果分析******************/
concurrentQueue 屬于并行隊(duì)列,執(zhí)行結(jié)果時(shí)序不一定
dispatch_async() 異步調(diào)用刻剥,不需要等待直接順序執(zhí)行遮咖,
所以,線程并非只有一個(gè)造虏,開(kāi)辟了多個(gè)線程并行處理任務(wù)御吞,輸出的結(jié)果的順序也不確定