最近看了GCD中的死鎖相關(guān)的內(nèi)容,現(xiàn)結(jié)合其他朋友的貢獻(xiàn)做一些分析和總結(jié)。
首先來(lái)看兩個(gè)概念: 任務(wù)和隊(duì)列
任務(wù)(什么block,函數(shù),以及隨便的代碼片段都可以稱為一個(gè)任務(wù))
任務(wù)分為同步任務(wù)和異步任務(wù):同步任務(wù)會(huì)阻塞當(dāng)前線程(其實(shí)這個(gè)任務(wù)也就是block中的內(nèi)容),等block中的任務(wù)執(zhí)行完成之后,當(dāng)前線程才會(huì)繼續(xù)往下執(zhí)行;異步任務(wù)不會(huì)阻塞當(dāng)前線程,也就是不需要等block中的任務(wù)執(zhí)行完成,當(dāng)前線程就能直接往下執(zhí)行。隊(duì)列(任務(wù)調(diào)度的規(guī)則)
隊(duì)列分為串行隊(duì)列和并行隊(duì)列:串行隊(duì)列中,GCD會(huì)按照先進(jìn)先出的原則,順序執(zhí)行放進(jìn)來(lái)的任務(wù);并行隊(duì)列中,GCD其實(shí)也是按照先進(jìn)先出的原則,只是每個(gè)任務(wù)可能被放進(jìn)了不同的線程,因?yàn)樗俣确浅?欤越o我們的感覺(jué)像是所有的任務(wù)同時(shí)執(zhí)行
兩個(gè)常用的隊(duì)列
- dispatch_get_global_queue: 全局并發(fā)隊(duì)列,可以同時(shí)提供多個(gè)線程執(zhí)行任務(wù)。
- dispatch_get_main_queue: 串行主隊(duì)列,只能提供一個(gè)主線程來(lái)執(zhí)行任務(wù)。
下面用實(shí)例來(lái)分析一下常見(jiàn)的死鎖問(wèn)題。
1.同步主隊(duì)列死鎖
- (void)gcdTest1 {
NSLog(@"1 - %@",[NSThread currentThread]); //1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2 - %@",[NSThread currentThread]); //2
});
NSLog(@"3 - %@",[NSThread currentThread]); //3
//打?。?
/*
解釋:dispatch_sync阻塞了當(dāng)前線程,跑去執(zhí)行任務(wù)2,
而任務(wù)2又是被放到主隊(duì)列里面,也就是要在主線程中執(zhí)行了,
但是它是放在了任務(wù)3的后面(不要以為任務(wù)2的代碼寫在了任務(wù)3的前面,任務(wù)2在隊(duì)列中就排在了任務(wù)3的前面)。
由于主隊(duì)列是一個(gè)串行隊(duì)列,任務(wù)2需要等任務(wù)3執(zhí)行完之后,才能被執(zhí)行,而此時(shí)線程已經(jīng)在任務(wù)3之前被阻塞住了,
所以任務(wù)3不可能被執(zhí)行,所以任務(wù)2也不可能被執(zhí)行
*/
}
2.同步全局并行隊(duì)列
- (void)gcdTest2 {
NSLog(@"1 - %@",[NSThread currentThread]); //1
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2 - %@",[NSThread currentThread]); //2 (通過(guò)打印可以發(fā)現(xiàn),任務(wù)2也是放在主線程中執(zhí)行的,這也就說(shuō)明了全局隊(duì)列是可以拿主線程來(lái)執(zhí)行相應(yīng)任務(wù)的)
});
NSLog(@"3 - %@",[NSThread currentThread]); //3
//打?。?,2,3
/*
解釋:首先dispatch_sync阻塞當(dāng)前線程,跑去執(zhí)行任務(wù)2,
任務(wù)2是被放到dispatch_get_global_queue這個(gè)并行隊(duì)列里面,
而任務(wù)3是在主隊(duì)列里面,也就是說(shuō)這兩個(gè)任務(wù)根本就不在同一個(gè)隊(duì)列,
就不存在一個(gè)先后順序的問(wèn)題,所以三個(gè)任務(wù)都會(huì)被執(zhí)行
*/
}
3.同步自定義串行隊(duì)列
- (void)gcdTest3 {
NSLog(@"1 - %@",[NSThread currentThread]); //1
dispatch_sync(dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL), ^{
NSLog(@"2 - %@",[NSThread currentThread]); //2
});
NSLog(@"3 - %@",[NSThread currentThread]);//3
//打印:1,2,3
/*
解釋:同上。也就是任務(wù)2和任務(wù)3兩個(gè)任務(wù)處于不同的隊(duì)列,所以沒(méi)有影響,都會(huì)被執(zhí)行。
*/
}
4.異步主隊(duì)列
- (void)gcdTest4 {
NSLog(@"1 - %@",[NSThread currentThread]); //1
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2 - %@",[NSThread currentThread]);//2 (這里打印的還是主線程,說(shuō)明異步并不代表要開(kāi)辟新的線程,)
});
NSLog(@"3 - %@",[NSThread currentThread]);//3
//打印:1,3,2
/*
解釋:dispatch_async是一個(gè)異步任務(wù),不會(huì)阻塞當(dāng)前線程,
所以3可以執(zhí)行,dispatch_get_main_queue是一個(gè)串行隊(duì)列,所以任務(wù)2是排在任務(wù)3的后面
*/
}
5.異步全局隊(duì)列
- (void)gcdTest5 {
NSLog(@"1 - %@",[NSThread currentThread]); //1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2 - %@",[NSThread currentThread]); //2
});
NSLog(@"3 - %@",[NSThread currentThread]); //3
//打印: 1,3,2
/*
解釋:這個(gè)跟上面那個(gè)的區(qū)別是:上面那個(gè)任務(wù)2是放到主隊(duì)列里面的,
肯定是排在任務(wù)3的后面,所以打印的結(jié)果是1,3,2.
但是這個(gè)任務(wù)2是放在一個(gè)全局隊(duì)列里面,而任務(wù)3是放到主隊(duì)列里面,
按理說(shuō)任務(wù)2和任務(wù)3是可以并行的,
也就是說(shuō)任務(wù)3和任務(wù)2的執(zhí)行順序可能不確定,但是我們通過(guò)打印發(fā)現(xiàn),
在執(zhí)行任務(wù)2的時(shí)候開(kāi)辟了新的線程,而開(kāi)辟新的線程需要額外的時(shí)間,所以任務(wù)2一般都會(huì)排在任務(wù)3的后面。
*/
}
6.異步并行隊(duì)列2
- (void)gcdTest6 {
NSLog(@"1 - %@",[NSThread currentThread]); //1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2 - %@",[NSThread currentThread]); //2
});
for (int i = 0; i<100; i++) { //3
NSLog(@"3 - %@",[NSThread currentThread]);
}
//打印:任務(wù)2穿插在任務(wù)3循環(huán)中間
/*
解釋:上面剛剛說(shuō)過(guò),任務(wù)2會(huì)開(kāi)辟新的線程,需要額外的時(shí)間,
但是任務(wù)3是一個(gè)大循環(huán),需要更多的時(shí)間。所以從理論上說(shuō),任務(wù)2和任務(wù)3的執(zhí)行順序還是不確定的
*/
}
7.交叉分析
- (void)gcdTest7 {
NSLog(@"1"); // 任務(wù)1
dispatch_async(dispatch_get_global_queue(0, 0), ^{ //block1
NSLog(@"2 - %@",[NSThread currentThread]); // 任務(wù)2
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3"); // 任務(wù)3
});
NSLog(@"4"); // 任務(wù)4
});
NSLog(@"5"); // 任務(wù)5
//打印:1,5,2,3,4
/*
解釋:任務(wù)1執(zhí)行完之后,dispatch_async異步去執(zhí)行另外的任務(wù),不會(huì)阻塞當(dāng)前線程,
所以任務(wù)5接著被執(zhí)行,接著任務(wù)2被執(zhí)行,dispatch_sync又阻塞了block1中的線程去執(zhí)行任務(wù)3,
而任務(wù)3是放在主隊(duì)列中的,跟任務(wù)4不是同一個(gè)隊(duì)列,所以任務(wù)3執(zhí)行完了之后,
任務(wù)4也被執(zhí)行了,所以打印的結(jié)果是: 1,5,2,3,4
*/
}
- (void)gcdTest8 {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1 - %@",[NSThread currentThread]); // 任務(wù)1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2 - %@",[NSThread currentThread]); // 任務(wù)2
});
NSLog(@"3"); // 任務(wù)3
});
NSLog(@"4"); // 任務(wù)4
while (1) {
}
NSLog(@"5"); // 任務(wù)5
//打印結(jié)果:4,1
/*
解釋:dispatch_async是一個(gè)異步任務(wù),不會(huì)阻塞當(dāng)前線程,
所以任務(wù)4會(huì)執(zhí)行,然后進(jìn)入block,執(zhí)行任務(wù)1,
接著dispatch_sync會(huì)阻塞block中線程,開(kāi)始執(zhí)行任務(wù)2,
而任務(wù)2是放到主隊(duì)列中的,排在任務(wù)5的后面,
而任務(wù)5又在死循環(huán)的后面,不可能被執(zhí)行,所以任務(wù)2不會(huì)被執(zhí)行,從而3也不會(huì)被完成,所以打印結(jié)果是4,1。
*/
}