發(fā)現(xiàn)自己是個懶人,當時注冊簡書,信誓旦旦的想著把平時總結(jié)的,用到的,解決的都記錄下來,回頭一看,平均一年寫了不到一篇,技術(shù)水準也不是很高。慚愧慚愧。不過,什么內(nèi)容網(wǎng)上都能搜羅出來一大堆,這篇文章,不講內(nèi)部實現(xiàn)(講的話我也說不太好,需要的同學請自行soso),只講直接可以使用的方式。
Dispatch_semaphore_t 信號量
GCD 信號量是一種異步轉(zhuǎn)同步的方式,平時還可能用到的另外一個點,就是加鎖。先說異步轉(zhuǎn)同步,比如需求有ABC三個異步請求,但是由于業(yè)務需求,我們需要A ->B->C依次執(zhí)行。信號量實現(xiàn)如下
{
//信號量初始化必須大于等于0, 因為dispatch_semaphore_wait 執(zhí)行的是-1操作。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("dispatch_barrier", DISPATCH_QUEUE_CONCURRENT);
//模仿異步請求
__block long x = 0;
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"============A");
x = dispatch_semaphore_signal(semaphore);//信號量 +1 進行了改變
NSLog(@"----- %ld",x);
});
NSLog(@"最先打印這里");
x = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//上面異步,此處信號量== 0,會進行等待操作
NSLog(@"----- %ld",x);
dispatch_async(queue, ^{
NSLog(@"============B");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"============C");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"------ 執(zhí)行結(jié)束了");
}
打印結(jié)果如下:
2019-08-29 15:25:39.308846+0800 DDDD[43496:3887181] 最先打印這里
2019-08-29 15:25:42.313244+0800 DDDD[43496:3887214] ============A
2019-08-29 15:25:42.313416+0800 DDDD[43496:3887214] ----- 1
2019-08-29 15:25:42.313422+0800 DDDD[43496:3887181] ----- 0
2019-08-29 15:25:42.313537+0800 DDDD[43496:3887216] ============B
2019-08-29 15:25:44.314613+0800 DDDD[43496:3887216] ============C
2019-08-29 15:25:44.314831+0800 DDDD[43496:3887181] ------ 執(zhí)行結(jié)束了
信號量操作用于加鎖,類似NSLock,使用起來更加方便,舉例如下:
{
//初始化為1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_queue_create("dispatch_barrier", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 3 ; i ++) {
dispatch_async(queue, ^{
//這一步相當于加鎖,初始化為1,此處-1操作后,會一直處于等待的狀態(tài)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"--- 打印何時走到這里,加鎖后,會一秒一走,未加鎖的話,會連續(xù)打印10次");
//這里可以進行耗時操作
[NSThread sleepForTimeInterval:1];
NSLog(@"------- %d",i);
//解鎖,繼續(xù)進行for循環(huán)
dispatch_semaphore_signal(semaphore);
});
}
}
打印結(jié)果如下,如果注釋掉加鎖代碼,打印如何,可以自己測試下
2019-08-29 15:47:06.486424+0800 DDDD[43708:3951203] --- 打印何時走到這里,加鎖后,會一秒一走,未加鎖的話,會連續(xù)打印10次
2019-08-29 15:47:07.490927+0800 DDDD[43708:3951203] ------- 0
2019-08-29 15:47:07.491189+0800 DDDD[43708:3951205] --- 打印何時走到這里,加鎖后,會一秒一走,未加鎖的話,會連續(xù)打印10次
2019-08-29 15:47:08.496292+0800 DDDD[43708:3951205] ------- 1
2019-08-29 15:47:08.496446+0800 DDDD[43708:3951204] --- 打印何時走到這里,加鎖后,會一秒一走,未加鎖的話,會連續(xù)打印10次
2019-08-29 15:47:09.500507+0800 DDDD[43708:3951204] ------- 2
Dispatch_group_t
dispatch_group_t 內(nèi)部實現(xiàn)就是對dispatch_semaphore_t 的封裝,dispatch_group_t 是一個初始值為LONG_MAX的信號量,dispatch_group_enter() 與 dispatch_semaphore_wait()相對應,dispatch_group_leave()與dispatch_semaphore_signal()相對應。dispatch_group判斷是否任務完成的標準就是判斷當前信號量的value是否回到了初始值。dispatch_group_enter()與dispatch_group_leave()可以嵌套使用,但必須成對出現(xiàn)。
開發(fā)中最常用的也就是多接口請求了。例如AB量個接口全部返回后再進行C操作,用信號量也可以實現(xiàn),但信號量是一種同步操作,AB沒有依賴關(guān)系的話,我們應該并發(fā)進行,此時信號量不能直接滿足需求,dispatch_group_t就可以了。代碼如下:
{
//創(chuàng)建一個并發(fā)的隊列,是并發(fā)的,如果是串行的,則和使用信號量結(jié)果一致了
dispatch_queue_t queue = dispatch_queue_create("dispatch_group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
//模仿延時
[NSThread sleepForTimeInterval:5];
NSLog(@"============A");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"============B");
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
//開發(fā)過程中,渲染過程要放到主線程完成
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"============C");
});
});
}
打印結(jié)果如下,大家可以看到打印的時間差
2019-08-29 16:35:28.655795+0800 DDDD[44188:4059760] ============B
2019-08-29 16:35:33.659865+0800 DDDD[44188:4059758] ============A
2019-08-29 16:35:33.660073+0800 DDDD[44188:4059697] ============C
下面考慮個問題,如果我們需要再AB異步延時執(zhí)行完之后,插入一個異步的D操作,然后再執(zhí)行C呢?如果要滿足AB并發(fā),單純的依賴dispatch_group_t是滿足不了的。
Dispatch_barrier
- dispatch_barrier_async()
-
dispatch_barrier_sync()
這是dispatch_barrier的兩個方法,具體的區(qū)別,可以下下面的代碼中體現(xiàn),需求如上所說,在AB 之后進行D的操作,最后再進行C的操作。代碼如下
{
//滿足 AB并發(fā),這里需要并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("dispatch_barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"============A");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"============B");
});
//dispatch_barrier_sync ---------
dispatch_barrier_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"=我是 barrier 插入的======= D");
});
NSLog(@"---------1");
dispatch_async(queue, ^{
NSLog(@"============C");
});
NSLog(@"---------2");
}
打印結(jié)果如下(dispatch_barrier_async)
2019-08-29 16:22:03.890942+0800 DDDD[44060:4022500] ---------1
2019-08-29 16:22:03.891097+0800 DDDD[44060:4022500] ---------2
2019-08-29 16:22:04.894166+0800 DDDD[44060:4022525] ============B
2019-08-29 16:22:06.895921+0800 DDDD[44060:4022522] ============A
2019-08-29 16:22:07.900699+0800 DDDD[44060:4022522] =我是 barrier 插入的======= D
2019-08-29 16:22:07.900881+0800 DDDD[44060:4022522] ============C
如果替換為dispatch_barrier_sync,打印結(jié)果如下
2019-08-29 16:25:21.341990+0800 DDDD[44093:4032181] ============B
2019-08-29 16:25:23.342618+0800 DDDD[44093:4032180] ============A
2019-08-29 16:25:24.342861+0800 DDDD[44093:4032061] =我是 barrier 插入的======= D
2019-08-29 16:25:24.343021+0800 DDDD[44093:4032061] ---------1
2019-08-29 16:25:24.343134+0800 DDDD[44093:4032061] ---------2
2019-08-29 16:25:24.343144+0800 DDDD[44093:4032180] ============C
對比打印結(jié)果,我們可以知道,dispatch_barrier_async與dispatch_barrier_sync的區(qū)別就是dispatch_barrier_sync會阻斷后面的任務插入隊列或者阻斷后面的任務執(zhí)行,直到自己block塊中的任務執(zhí)行完成后,才會把后面的任務插入隊列或者執(zhí)行后面的操作。