這里如果我將22222上面的更改為dispatch_sync(q, ^{ ,請問執(zhí)行結(jié)果是什么洲劣?
答案是執(zhí)行到這里崩潰了瓢颅,我們來分析一下,為什么這里會崩潰熙兔?很多博客上面都有說會崩潰
但是理由很牽強悲伶。大概是下面截圖這個意思。
老實說住涉,這個說法很牽強麸锉。看這段我自己都不能說服我自己舆声,下面是唯一能說服我的帖子花沉,是從源碼的角度來說的。
一個曲高和寡語言水平很捉急的大神給出的源碼
但是他的分析太難看懂了媳握,主要是因為語文水平很捉急碱屁。這里我?guī)痛蠹铱偨Y(jié)一下。
實際上GCD里面關(guān)于實現(xiàn)同步隊列使用到的是信號量毙芜,模擬出來大概是這樣子的
@synchronized (self) {
if (self.array.count synsc block里面需要被執(zhí)行的
//一個同步事件可以分為兩部分. 第一wait queue, 第二, 派發(fā)事件.
//此處為了方便, 兩行代碼位置是反的. 但是由于async 派遣, 本身就拼到了隊列末尾. 所以
//從實際執(zhí)行角度, 順序是沒有問題的. 完整模擬了. dispatch源碼中對事件的執(zhí)行模式和 同步派遣到本身隊列的死鎖問題.
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self.array removeLastObject];
NSLog(@"\n");
NSLog(@"消費了 同步!!!!!!事件");
NSLog(@"\n");
syncEvent = NO;
}else{
//普通消費.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@synchronized (self) {
[self.array removeLastObject];
NSLog(@"消費了一個異步事件.");
}
});
}
}
想要看懂上面這段代碼忽媒,你需要反復(fù)理解下面幾句話,我當(dāng)時想了一下午才想通腋粥。
串行隊列里面只可能有一個線程晦雨,并行隊列里面可能有多個架曹。
隊列里面可能沒有線程,線程總是跑來跑去的闹瞧。
不管是同步函數(shù)或者是異步函數(shù)绑雄,都會將block里面的內(nèi)容派遣到對應(yīng)的隊列的最下面。
同步函數(shù)里面維護了一套信號量奥邮,信號量的single操作被套在異步函數(shù)里面
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});
同步函數(shù)的的其他操作在同步函數(shù)所處的外面的隊列里面去執(zhí)行万牺,只有
dispatch_semaphore_signal(semaphore);
在同步函數(shù)鎖包裹的隊列里面去執(zhí)行。
綜上所述洽腺,這行代碼將信號量的++事件放到了queue隊列的最后脚粟。如果同步函數(shù)外面沒有對queue做派遣動作,不會死鎖蘸朋。
我們將這些理論放到實際例子里面去解釋
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1111");
});
NSLog(@"222");
}
外面是主隊列核无,主隊列里面是主線程,同步函數(shù)將NSLog(@"1111"); 壓倒主隊列的底部了藕坯。里外都是主隊列团南,事件順序執(zhí)行。
同步函數(shù)底層按照順序異步函數(shù)將dispatch_semaphore_signal(semaphore);
壓在queue(主隊列)的最下面
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});
然后執(zhí)行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
信號量--為-1炼彪,阻塞當(dāng)前線程
吐根,這樣程序員永遠(yuǎn)都執(zhí)行不到dispatch_semaphore_signal(semaphore)
思考
這個例子不知道大家看懂了沒有,是不是感覺這樣分析的話辐马,只要使用同步函數(shù)都會被死鎖拷橘。
其實同步函數(shù)的底層下面這個函數(shù)能夠執(zhí)行到線程就不會被鎖住
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});
如何能執(zhí)行dispatch_semaphore_signal(semaphore);
信號量的++操作被壓在queue的最下面了,只要同步函數(shù)的執(zhí)行的queue和外面的queue不一致齐疙,這里就會被執(zhí)行了膜楷。
看這個例子,我們將同步函數(shù)派遣的主隊列換成一個新的串行隊列
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_queue_create("1111", DISPATCH_QUEUE_SERIAL), ^{
NSLog(@"1111");
});
NSLog(@"222");
}
這樣同步函數(shù)的底層在主隊列里面執(zhí)行下面信號量--
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
主隊列里面執(zhí)行異步函數(shù)+串行隊列贞奋,將信號量++壓在串行隊列的底部,串行隊列唯一事件為信號量++函數(shù)穷绵,于是執(zhí)行解鎖
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});
再來一個例子
dispatch_queue_t q = dispatch_queue_create("1111111", DISPATCH_QUEUE_SERIAL);
dispatch_sync(q, ^{
NSLog(@"11111");
dispatch_sync(q, ^{
NSLog(@"22222");
});
NSLog(@"33333");
});
NSLog(@"44444");
NSLog(@"5555");
第一層同步函數(shù)處在主隊列里面轿塔,
同步函數(shù)的底層主隊列里面執(zhí)行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
在新的串行隊列里面執(zhí)行
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});
這兩步操作分開在不同的隊列里面,都能執(zhí)行仲墨,所以不會死鎖
第二層同步函數(shù)處在q隊列里面
同步函數(shù)的底層q串行隊列里面執(zhí)行勾缭,鎖住了q隊列,q隊列無法執(zhí)行其他事件
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
在q串行隊列里面執(zhí)行目养,被壓入隊列事件中俩由,但是因為信號量鎖住線程gg
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});