在上篇文章多線程中沾谜,我們簡單介紹了下3種創(chuàng)建多線程方法的區(qū)別膊毁。在一般公司的面試當(dāng)做,大多也只會(huì)問下他們的區(qū)別基跑。
請看下面這道面試題婚温,輸出結(jié)果會(huì)是什么了?
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
是不是看著有點(diǎn)眼熟媳否,但又有點(diǎn)不確定栅螟。因?yàn)槲覀兤綍r(shí)中都是先異步,再同步篱竭,如下這樣寫的
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
這樣看著是不是舒服多了力图,那么這種輸出結(jié)果已經(jīng)是什么了?
運(yùn)行下就會(huì)發(fā)現(xiàn)掺逼,報(bào)異常了吃媒。是不是覺得奇怪, 好像平時(shí)這樣寫代碼也沒有啥大問題啊。
再仔細(xì)回想赘那、仔細(xì)觀察下你會(huì)發(fā)現(xiàn)異步那個(gè)地方和我們平時(shí)寫的不一樣
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
這種應(yīng)該是我們之前經(jīng)常寫的刑桑,一點(diǎn)問題都沒有。那么問題在哪里了募舟?
我們先看個(gè)例子祠斧,運(yùn)行正常
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
再看個(gè)例子,也會(huì)發(fā)現(xiàn)能正常運(yùn)行拱礁。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
對比這4個(gè)例子琢锋,猜測應(yīng)該是如下這句代碼有問題
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
我們單獨(dú)把它提取出來,運(yùn)行發(fā)現(xiàn)果然是有問題。
我們運(yùn)行下最開始的那道面試題,錯(cuò)誤一樣。
好了侠草,找到出問題的點(diǎn)了,就是在同步線程里面執(zhí)行dispatch_get_main_queue()時(shí)會(huì)發(fā)送線程卡死的現(xiàn)象恰起。
我們都是在主隊(duì)列中去更新UI惠赫,即我們的界面都是由系統(tǒng)在主隊(duì)列中維護(hù)的,我們在viewDidLoad方法中使用如下GCD方法
dispatch_sync(dispatch_get_main_queue(), ^{
});
就會(huì)出錯(cuò)仆潮,我們知道同步線程不會(huì)去創(chuàng)建新的線程宏蛉。
驗(yàn)證如下
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i <10; i++)
{
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@" %@", [NSThread currentThread]);
});
}
}
輸出如下,會(huì)看到為同一個(gè)線程
2017-08-28 16:28:00.431 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.432 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.434 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.434 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.434 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.435 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.435 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.435 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.435 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.436 測試Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
所以我們是和UI在同一個(gè)線程里面性置,并且再次去執(zhí)行一個(gè)主隊(duì)列任務(wù)拾并。我們知道dispatch_get_main_queue()這個(gè)主隊(duì)列是一個(gè)串行的隊(duì)列,而串行隊(duì)列的基礎(chǔ)法則時(shí)FIFO(先進(jìn)先出)鹏浅。所以這時(shí)我們就找到了卡死的問題點(diǎn)了:系統(tǒng)維護(hù)的dispatch_get_main_queue()這個(gè)隊(duì)列里面在執(zhí)行viewDidLoad方法嗅义,在viewDidLoad中又再次在dispatch_get_main_queue()這個(gè)相同的隊(duì)列里面執(zhí)行block方法。由于串行隊(duì)列FIFO原則隐砸,系統(tǒng)維護(hù)的dispatch_get_main_queue()先進(jìn)棧之碗,所以要先執(zhí)行完畢后,再執(zhí)行后進(jìn)棧的隊(duì)列任務(wù)季希,而系統(tǒng)維護(hù)的dispatch_get_main_queue()執(zhí)行完的條件時(shí)viewDidLoad方法執(zhí)行完畢褪那,所以系統(tǒng)維護(hù)的dispatch_get_main_queue()會(huì)等待dispatch_sync調(diào)用的dispatch_get_main_queue()執(zhí)行完畢,dispatch_sync調(diào)用的ispatch_get_main_queue()又在等待先進(jìn)棧的系統(tǒng)維護(hù)的dispatch_get_main_queue()執(zhí)行完畢式塌,這樣就陷入死循環(huán)博敬。
所以我們得出一個(gè)結(jié)論:在同一個(gè)線程里面,調(diào)用同一個(gè)串行隊(duì)列會(huì)發(fā)送死鎖現(xiàn)象峰尝。
現(xiàn)在我們實(shí)驗(yàn)下
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t q = dispatch_queue_create("hhhhh", DISPATCH_QUEUE_SERIAL);
dispatch_async(q, ^{
NSLog(@" %@----111", [NSThread currentThread]);
dispatch_sync(q, ^{
NSLog(@" %@----2222", [NSThread currentThread]);
});
});
}
運(yùn)行解決正如我們所總結(jié)的那樣
我們現(xiàn)在實(shí)驗(yàn)下并行隊(duì)列
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t q = dispatch_queue_create("hhhhh", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q, ^{
NSLog(@" %@----111", [NSThread currentThread]);
dispatch_sync(q, ^{
NSLog(@" %@----2222", [NSThread currentThread]);
});
});
發(fā)現(xiàn)能正常運(yùn)行
最后總結(jié)下:在一個(gè)線程內(nèi)部包含同步線程+串行隊(duì)列一起使用時(shí)要注意死鎖偏窝。