在開發(fā)中畏腕,經(jīng)常會碰到這樣的情況:
1.同一個頁面上,有好幾個網(wǎng)絡(luò)請求,需要等到所有的網(wǎng)絡(luò)請求都調(diào)用結(jié)束再進行下一步操作温艇。
2.同一個頁面上逻恐,有好幾個網(wǎng)絡(luò)請求像吻,但第二個接口依賴于第一個接口,必須等到第二個調(diào)用完再調(diào)用复隆。
處理上述問題的方式很多拨匆,可以在接口回調(diào)里再去調(diào)下一個接口;可以自己加變量來判斷調(diào)用順序挽拂。但其實GCD
已經(jīng)提供了很好的方法來解決這一問題惭每,可以用dispatch_group_async
和dispatch_semaphore_t
來操作,具體看一下實例亏栈。
多個請求都結(jié)束再執(zhí)行下一步
使用dispatch_group_async
//mainthread
dispatch_group_t group = dispatch_group_create();
NSLog(@"!-----1");
dispatch_group_enter(group);
[[NetworkMgr sharedMgr] refreshServerAppConfigWithCompleteHandle:^{//main thread block
NSLog(@"!-----2");
dispatch_group_leave(group);
}];
NSLog(@"!-----3");
dispatch_group_enter(group);
[[NetworkMgr sharedMgr] refreshBussinessRouterWithCompleteHandle:^{//main thread block
NSLog(@"!-----4");
dispatch_group_leave(group);
}];
NSLog(@"!-----5");
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"!-----6");
if (self.homeAction) {
self.homeAction(0, nil);
}
});
執(zhí)行順序
[37063:6826480] !-----1
[37063:6826480] !-----3
[37063:6826480] !-----5
[37063:6826480] !-----2
[37063:6826480] !-----4
[37063:6826480] !-----6
在執(zhí)行每一個網(wǎng)絡(luò)請求之前台腥,先dispatch_group_enter
,在請求回調(diào)時绒北,再dispatch_group_leave
黎侈,當兩組請求都結(jié)束后,會自動觸發(fā)dispatch_group_notify
闷游,實現(xiàn)self.homeAction
在兩個請求之后再調(diào)用蜓竹。
使用dispatch_semaphore_t
NSLog(@"!-----1");
dispatch_semaphore_t semphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
[[NetworkMgr sharedMgr] refreshServerAppConfigWithCompleteHandle:^{//main thread block
NSLog(@"!-----2");
dispatch_semaphore_signal(semphore);
}];
NSLog(@"!-----3");
[[NetworkMgr sharedMgr] refreshBussinessRouterWithCompleteHandle:^{//main thread block
NSLog(@"!-----4");
dispatch_semaphore_signal(semphore);
}];
NSLog(@"!-----5");
dispatch_async(queue, ^{
dispatch_semaphore_wait(semphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semphore, DISPATCH_TIME_FOREVER);
NSLog(@"!-----6");
if (self.homeAction) {
dispatch_async(dispatch_get_main_queue(), ^{
self.homeAction(0, nil);
});
}
});
執(zhí)行順序
[36999:6817621] !-----1
[36999:6817621] !-----3
[36999:6817621] !-----5
[36999:6817621] !-----2
[36999:6817621] !-----4
[36999:6817985] !-----6
也能夠?qū)崿F(xiàn)在兩個網(wǎng)絡(luò)回調(diào)結(jié)束之后箕母,再執(zhí)行self.homeAction
。剛開始的信號量為0俱济,在創(chuàng)建的queue
中嘶是,兩個請求都沒返回的情況下,會等在第一個dispatch_semaphore_wait
蛛碌。當?shù)谝粋€請求結(jié)束后聂喇,發(fā)出dispatch_semaphore_signal
,這時跳過第一個dispatch_semaphore_wait
蔚携。第二個請求結(jié)束后希太,發(fā)出dispatch_semaphore_signal
,跳過第二個dispatch_semaphore_wait
酝蜒。這樣就實現(xiàn)了在兩個網(wǎng)絡(luò)回調(diào)結(jié)束之后誊辉,再執(zhí)行self.homeAction
。
不過需要注意的是亡脑,dispatch_semaphore_wait
會使當前線程等待堕澄。就上述代碼而言,dispatch_semaphore_wait
所在的線程是子線程霉咨,而網(wǎng)絡(luò)回調(diào)的線程是在主線程蛙紫,當調(diào)用到dispatch_semaphore_wait
會使子線程發(fā)生等待,知道接收到主線程的兩個dispatch_semaphore_signal
途戒,等待才會取消坑傅。如果把queue
改成主線程,就會阻塞喷斋,因為主線程會一直等待唁毒,請求回調(diào)的block也是在主線程,永遠不會執(zhí)行星爪。
多個請求按順序同步執(zhí)行
使用dispatch_semaphore_t
dispatch_semaphore_t semphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NetworkMgr sharedMgr] refreshServerAppConfigWithCompleteHandle:^{//main thread block
NSLog(@"!-----1");
dispatch_semaphore_signal(semphore);
}];
});
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semphore, DISPATCH_TIME_FOREVER);
[[NetworkMgr sharedMgr] refreshBussinessRouterWithCompleteHandle:^{//main thread block
NSLog(@"!-----2");
dispatch_semaphore_signal(semphore);
}];
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semphore, DISPATCH_TIME_FOREVER);
NSLog(@"!-----3");
if (self.homeAction) {
dispatch_async(dispatch_get_main_queue(), ^{
self.homeAction(0, nil);
});
}
});
執(zhí)行順序
[37297:6848505] !-----1
[37297:6848505] !-----2
[37297:6848808] !-----3
打印結(jié)束顯示枉证,保證了第二個請求回調(diào)在第一個之后。剛開始創(chuàng)建的信號量為1移必,執(zhí)行第一個請求,回調(diào)結(jié)束后毡鉴,發(fā)出dispatch_semaphore_signal
崔泵,才會執(zhí)行第二個請求,回調(diào)結(jié)束后才會執(zhí)行self.homeAction
猪瞬。
這里也是要保證dispatch_semaphore_wait
所在的線程必須和dispatch_semaphore_signal
所在的線程不同憎瘸。
dispatch_semaphore_create
dispatch_semaphore_t dispatch_semaphore_create(long value);
傳入的參數(shù)為long,輸出一個dispatch_semaphore_t類型且值為value的信號量陈瘦。
值得注意的是幌甘,這里的傳入的參數(shù)value必須大于或等于0,否則dispatch_semaphore_create會返回NULL。
dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
這個函數(shù)會使傳入的信號量dsema的值加1锅风;
dispatch_semaphore_signal的返回值為long類型酥诽,當返回值為0時表示當前并沒有線程等待其處理的信號量,其處理
的信號量的值加1即可皱埠。當返回值不為0時肮帐,表示其當前有(一個或多個)線程等待其處理的信號量,并且該函數(shù)喚醒了一
個等待的線程(當線程有優(yōu)先級時边器,喚醒優(yōu)先級最高的線程训枢;否則隨機喚醒)。
dispatch_semaphore_wait的返回值也為long型忘巧。當其返回0時表示在timeout之前恒界,該函數(shù)所處的線程被成功喚醒。
當其返回不為0時砚嘴,表示timeout發(fā)生十酣。
dispatch_semaphore_wait
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
這個函數(shù)會使傳入的信號量dsema的值減1枣宫;
這個函數(shù)的作用是這樣的婆誓,如果dsema信號量的值大于0,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語句也颤,并且將信號量的值減1洋幻;
如果desema的值為0,那么這個函數(shù)就阻塞當前線程等待timeout(注意timeout的類型為dispatch_time_t翅娶,
不能直接傳入整形或float型數(shù))文留,如果等待的期間desema的值被dispatch_semaphore_signal函數(shù)加1了,
且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號量竭沫,那么就繼續(xù)向下執(zhí)行并將信號量減1燥翅。
如果等待期間沒有獲取到信號量或者信號量的值一直為0,那么等到timeout時蜕提,其所處線程自動執(zhí)行其后語句森书。