以下三種方法通過(guò)線程的依賴關(guān)系實(shí)現(xiàn)線程同步:
1.組隊(duì)列(dispatch_group_t)
2.阻塞任務(wù)(dispatch_barrier_(a)sync)
3.信號(hào)量機(jī)制(dispatch_semaphore)
1.組隊(duì)列(dispatch_group_t):
舉一個(gè)例子:用戶下載一個(gè)圖片震庭,圖片很大,需要分成很多份進(jìn)行下載压语,使用GCD應(yīng)該如何實(shí)現(xiàn)榄鉴?使用什么隊(duì)列?
使用Dispatch Group追加block到Global Group Queue渡贾,這些block如果全部執(zhí)行完畢逗宜,就會(huì)執(zhí)行通過(guò)dispatch_group_notify添加到主隊(duì)列中的block,進(jìn)行圖片的合并處理。
?dispatch_group_t group = dispatch_group_create();
??? dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
??????? //請(qǐng)求1
??????? [self request_A];
??? });
??? dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
??????? //請(qǐng)求2
??????? [self request_B];
??? });
??? dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
??????? //請(qǐng)求3
???????? [self request_C];
??? });
??? dispatch_group_notify(group, dispatch_get_main_queue(), ^{
??????? //界面刷新
??????? NSLog(@"任務(wù)均完成纺讲,刷新界面");
??? });
2.阻塞任務(wù)(dispatch_barrier):
通過(guò)dispatch_barrier_async添加的操作會(huì)暫時(shí)阻塞當(dāng)前隊(duì)列擂仍,即等待前面的并發(fā)操作都完成后執(zhí)行該阻塞操作,待其完成后后面的并發(fā)操作才可繼續(xù)熬甚》暧妫可以將其比喻為一根霸道的獨(dú)木橋,是并發(fā)隊(duì)列中的一個(gè)并發(fā)障礙點(diǎn)乡括,或者說(shuō)中間瓶頸肃廓,臨時(shí)阻塞并獨(dú)占。注意dispatch_barrier_async只有在并發(fā)隊(duì)列中才能起作用诲泌,在串行隊(duì)列中隊(duì)列本身就是獨(dú)木橋盲赊,將失去其意義。
可見使用dispatch_barrier_async可以實(shí)現(xiàn)類似dispatch_group_t組調(diào)度的效果,同時(shí)主要的作用是避免數(shù)據(jù)競(jìng)爭(zhēng)敷扫,高效訪問數(shù)據(jù)哀蘑。
/* 創(chuàng)建并發(fā)隊(duì)列 */
dispatch_queue_t concurrentQueue=dispatch_queue_create("test.concurrent.queue",DISPATCH_QUEUE_CONCURRENT);
/* 添加兩個(gè)并發(fā)操作A和B,即A和B會(huì)并發(fā)執(zhí)行 */
dispatch_async(concurrentQueue,^({NSLog(@"OperationA");});
dispatch_async(concurrentQueue,^(){NSLog(@"OperationB");});
/* 添加barrier障礙操作葵第,會(huì)等待前面的并發(fā)操作結(jié)束绘迁,并暫時(shí)阻塞后面的并發(fā)操作直到完成*/
dispatch_barrier_async(concurrentQueue,^(){NSLog(@"OperationBarrier!");});
/* 繼續(xù)添加并發(fā)操作C和D,要等待barrier障礙操作結(jié)束才能開始*/
dispatch_async(concurrentQueue,^({NSLog(@"OperationC");});
dispatch_async(concurrentQueue,^(){NSLog(@"OperationD");});
執(zhí)行結(jié)果如下:
2017-04-04 12:25:02.344 SingleView[12818:3694480] OperationB
2017-04-04 12:25:02.344 SingleView[12818:3694482] OperationA
2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationBarrier!
2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationD
2017-04-04 12:25:02.345 SingleView[12818:3694480] OperationC
3.信號(hào)量機(jī)制(dispatch_semaphore):
信號(hào)量機(jī)制主要是通過(guò)設(shè)置有限的資源數(shù)量來(lái)控制線程的最大并發(fā)數(shù)量以及阻塞線程實(shí)現(xiàn)線程同步等羹幸。
GCD中使用信號(hào)量需要用到三個(gè)函數(shù):
dispatch_semaphore_create用來(lái)創(chuàng)建一個(gè)semaphore信號(hào)量并設(shè)置初始信號(hào)量的值脊髓;
dispatch_semaphore_signal發(fā)送一個(gè)信號(hào)讓信號(hào)量增加1(對(duì)應(yīng)PV操作的V操作);
dispatch_semaphore_wait等待信號(hào)使信號(hào)量減1(對(duì)應(yīng)PV操作的P操作)栅受;
那么如何通過(guò)信號(hào)量來(lái)實(shí)現(xiàn)線程同步呢将硝?下面介紹使用GCD信號(hào)量來(lái)實(shí)現(xiàn)任務(wù)間的依賴和最大并發(fā)任務(wù)數(shù)量的控制。
使用信號(hào)量實(shí)現(xiàn)任務(wù)2依賴于任務(wù)1屏镊,即任務(wù)2要等待任務(wù)1結(jié)束才開始執(zhí)行:
方法很簡(jiǎn)單依疼,創(chuàng)建信號(hào)量并初始化為0,讓任務(wù)2執(zhí)行前等待信號(hào)而芥,實(shí)現(xiàn)對(duì)任務(wù)2的阻塞律罢。然后在任務(wù)1完成后再發(fā)送信號(hào),從而任務(wù)2獲得信號(hào)開始執(zhí)行棍丐。需要注意的是這里任務(wù)1和2都是異步提交的误辑,如果沒有信號(hào)量的阻塞,任務(wù)2是不會(huì)等待任務(wù)1的,實(shí)際上這里使用信號(hào)量實(shí)現(xiàn)了兩個(gè)任務(wù)的同步歌逢。
通過(guò)打印的時(shí)間可以看到任務(wù)2是在任務(wù)1結(jié)束后緊接著執(zhí)行的:
通過(guò)信號(hào)量控制最大并發(fā)數(shù)量:
通過(guò)信號(hào)量控制最大并發(fā)數(shù)量的方法為:創(chuàng)建信號(hào)量并初始化信號(hào)量為想要控制的最大并發(fā)數(shù)量巾钉,例如想要保證最大并發(fā)數(shù)為5,則信號(hào)量初始化為5秘案。然后在每個(gè)新任務(wù)執(zhí)行前進(jìn)行P操作砰苍,等待信號(hào)使信號(hào)量減1潦匈;每個(gè)任務(wù)結(jié)束后進(jìn)行V操作,發(fā)送信號(hào)使信號(hào)量加1赚导。這樣即可保證信號(hào)量始終在5以內(nèi)茬缩,當(dāng)前最多也只有5個(gè)以內(nèi)的任務(wù)在并發(fā)執(zhí)行。
打印結(jié)果為每次開啟五個(gè)并發(fā)任務(wù):