GCD柵欄函數(shù)
僅在自己創(chuàng)建的并發(fā)隊列上有效,在全局(Global)并發(fā)隊列焦蘑、串行隊列上链蕊,效果跟dispatch_(a)sync效果一樣
我們有時需要異步執(zhí)行兩組操作请祖,而且第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作穿肄。這樣我們就需要一個相當(dāng)于柵欄一樣的一個方法將兩組異步執(zhí)行的操作組給分割起來年局,當(dāng)然這里的操作組里可以包含一個或多個任務(wù)。這就需要用到dispatch_barrier_async方法在兩個操作組間形成柵欄咸产。
dispatch_queue_t concurrentQ = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"beagin %@",[NSThread currentThread]);
dispatch_async(concurrentQ, ^{
sleep(3);
NSLog(@"first %@",[NSThread currentThread]);
});
dispatch_async(concurrentQ, ^{
sleep(3);
NSLog(@"section %@",[NSThread currentThread]);
});
dispatch_barrier_async(concurrentQ, ^{
sleep(2);
NSLog(@"barrier %@",[NSThread currentThread]);
});
dispatch_async(concurrentQ, ^{
sleep(2);
NSLog(@"three %@",[NSThread currentThread]);
});
dispatch_async(concurrentQ, ^{
sleep(2);
NSLog(@"four %@",[NSThread currentThread]);
});
/*輸出結(jié)果:
2017-10-10 22:02:42.083206+0800 GCDLearnAdvanced[8535:1603419] beagin <NSThread: 0x60400007bcc0>{number = 1, name = main}
2017-10-10 22:02:45.086151+0800 GCDLearnAdvanced[8535:1603466] section <NSThread: 0x6080002630c0>{number = 3, name = (null)}
2017-10-10 22:02:45.086187+0800 GCDLearnAdvanced[8535:1603468] first <NSThread: 0x60400026c700>{number = 4, name = (null)}
2017-10-10 22:02:47.088121+0800 GCDLearnAdvanced[8535:1603466] barrier <NSThread: 0x6080002630c0>{number = 3, name = (null)}
2017-10-10 22:02:49.089080+0800 GCDLearnAdvanced[8535:1603466] three <NSThread: 0x6080002630c0>{number = 3, name = (null)}
2017-10-10 22:02:49.093410+0800 GCDLearnAdvanced[8535:1603468] four <NSThread: 0x60400026c700>{number = 4, name = (null)}
*/
可以看到這個方法會阻塞這個queue(注意是阻塞 queue 矢否,而不是阻塞當(dāng)前線程),一直等到這個 queue 中排在它前面的任務(wù)都執(zhí)行完成后才會開始執(zhí)行自己脑溢,并且當(dāng)自己 同步執(zhí)行 完畢后兴喂,再會取消阻塞,使這個 queue 中排在它后面的任務(wù)繼續(xù)執(zhí)行焚志。
GCD延時提交
//創(chuàng)建串行隊列
dispatch_queue_t serialQ = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
NSLog(@"beagin %@",[NSThread currentThread]);
dispatch_async(serialQ, ^{
NSLog(@"task 開始 %@",[NSThread currentThread]);
sleep(10);
NSLog(@"task 完成 %@",[NSThread currentThread]);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), serialQ, ^{
NSLog(@"After...%@",[NSThread currentThread]);
sleep(2);
NSLog(@"After.22.%@",[NSThread currentThread]);
});
/*輸出結(jié)果:
2017-10-12 15:11:27.109950+0800 GCDLearnAdvanced[2409:593908] beagin <NSThread: 0x60400006b000>{number = 1, name = main}
2017-10-12 15:11:27.110231+0800 GCDLearnAdvanced[2409:593956] task 開始 <NSThread: 0x60800007fdc0>{number = 3, name = (null)}
2017-10-12 15:11:37.114435+0800 GCDLearnAdvanced[2409:593956] task 完成 <NSThread: 0x60800007fdc0>{number = 3, name = (null)}
2017-10-12 15:11:37.114603+0800 GCDLearnAdvanced[2409:593956] After...<NSThread: 0x60800007fdc0>{number = 3, name = (null)}
2017-10-12 15:11:39.118761+0800 GCDLearnAdvanced[2409:593956] After.22.<NSThread: 0x60800007fdc0>{number = 3, name = (null)}
*/
從打印輸出可以看出衣迷,dispatch_after只是延時提交block,并不是延時后立即執(zhí)行酱酬。所以想用dispatch_after精確控制運行狀態(tài)的時候需要注意下
GCD dispatch_apply
NSLog(@"beagin %@",[NSThread currentThread]);
dispatch_apply(3, dispatch_get_global_queue(0, 0), ^(size_t index) {
sleep(3);
NSLog(@"current%@ %@",@(index),[NSThread currentThread]);
});
NSLog(@"end %@",[NSThread currentThread]);
/*輸出結(jié)果:
2017-10-11 14:19:55.146699+0800 GCDLearnAdvanced[1728:463964] beagin <NSThread: 0x60c00006dac0>{number = 1, name = main}
2017-10-11 14:19:58.148147+0800 GCDLearnAdvanced[1728:463964] current0 <NSThread: 0x60c00006dac0>{number = 1, name = main}
2017-10-11 14:19:58.148168+0800 GCDLearnAdvanced[1728:464015] current1 <NSThread: 0x604000070b00>{number = 3, name = (null)}
2017-10-11 14:19:58.148185+0800 GCDLearnAdvanced[1728:464021] current2 <NSThread: 0x608000278280>{number = 4, name = (null)}
2017-10-11 14:19:58.148291+0800 GCDLearnAdvanced[1728:463964] end <NSThread: 0x60c00006dac0>{number = 1, name = main}
*/
從輸出結(jié)果看出壶谒,beagin到end中間任務(wù)共耗時3s。所以當(dāng)數(shù)組內(nèi)元素需要循環(huán)處理且比較耗時可以使用這個函數(shù)
GCD Dispatch Source
定時器案例:
NSLog(@"beagin %@",[NSThread currentThread]);
__block NSInteger timeout = 3;
dispatch_queue_t globalQ = dispatch_get_global_queue(0, 0);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, globalQ);
dispatch_source_set_timer(source, dispatch_walltime(NULL,0), 1.0*NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(source, ^{
if (timeout<=0) {
dispatch_source_cancel(source);
NSLog(@"end %@",[NSThread currentThread]);
}else{
timeout --;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"現(xiàn)在秒 %@",@(timeout));
});
}
});
dispatch_resume(source);
/*輸出結(jié)果:
2017-10-12 16:00:33.139472+0800 GCDLearnAdvanced[2717:646701] beagin <NSThread: 0x60000006a040>{number = 1, name = main}
2017-10-12 16:00:33.167203+0800 GCDLearnAdvanced[2717:646701] 現(xiàn)在秒 2
2017-10-12 16:00:34.140082+0800 GCDLearnAdvanced[2717:646701] 現(xiàn)在秒 1
2017-10-12 16:00:35.140850+0800 GCDLearnAdvanced[2717:646701] 現(xiàn)在秒 0
2017-10-12 16:00:36.140079+0800 GCDLearnAdvanced[2717:646756] end <NSThread: 0x608000460a00>{number = 3, name = (null)}
*/
進(jìn)度條案例:
dispatch_queue_t globalQ = dispatch_get_global_queue(0, 0);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, globalQ);
__block NSUInteger totalComplete = 0;
dispatch_source_set_event_handler(source, ^{
NSUInteger value = dispatch_source_get_data(source);
totalComplete += value;
NSLog(@"進(jìn)度 %@ ",@((CGFloat)totalComplete/100));
});
dispatch_resume(source);
for (NSInteger i=0; i<100; i++) {
dispatch_async(globalQ, ^{
dispatch_source_merge_data(source, 1);
NSLog(@"線程:%@~~~~~~~~~~~~i = %@", [NSThread currentThread],@(i));
});
}
//輸出結(jié)果: ---太長膳沽,就不上了
從打印上可以看到進(jìn)度打印并不是隨線程打印連續(xù)的顯示汗菜,是間斷的让禀。原因:DISPATCH_SOURCE_TYPE_DATA_ADD是將觸發(fā)結(jié)果相加,最后統(tǒng)一執(zhí)行響應(yīng)陨界。如果dispatch_source_merge_data之間間隔時間越長巡揍,則每次觸發(fā)都會響應(yīng),但是如果間隔的時間很短菌瘪,則會將觸發(fā)后的結(jié)果相加后統(tǒng)一觸發(fā)腮敌。利用這一特性可以用來更新進(jìn)度條,因為沒必要每次進(jìn)度觸發(fā)都響應(yīng)俏扩。
GCD只執(zhí)行一次
//單例模式
static BaseViewController *instance;
+(instancetype)shareSingleMethod{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc]init];
});
return instance;
}
dispatch_once_t必須是全局或static變量糜工。其實就是保證dispatch_once_t只有一份實例
GCD信號量
//創(chuàng)建信號量,參數(shù):信號量的初值录淡,如果小于0則會返回NULL
dispatch_semaphore_create(信號量值)
//等待降低信號量
dispatch_semaphore_wait(信號量捌木,等待時間)
//提高信號量
dispatch_semaphore_signal(信號量)
- 信號量的工作原理:
如果初始semaphore信號量的值 >= 1時:對semaphore計數(shù)進(jìn)行減1,然后dispatch_semaphore_wait 函數(shù)返回。該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語句嫉戚。
如果初始semaphore信號量的值 = 0時:那么就阻塞該函數(shù)所處的線程,阻塞時長為timeout指定的時間刨裆。如果阻塞時間內(nèi)semaphore的值被dispatch_semaphore_signal函數(shù)加1了,該函數(shù)所處線程獲得了信號量被喚醒彬檀。然后對semaphore計數(shù)進(jìn)行減1并返回帆啃,繼續(xù)向下執(zhí)行。如果阻塞時間內(nèi)沒有獲取到信號量喚醒線程或者信號量的值一直為0凤覆,那么就要等到指定的阻塞時間后链瓦,該函數(shù)所處線程才繼續(xù)向下執(zhí)行拆魏。
案例同步獲取定位信息(以百度地圖為例)
- (NSString *)getLocalLatAndLon{
[_locService startUserLocationService];
dispatch_async(dispatch_get_main_queue(), ^{
hud=[MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.label.text = NSLocalizedString(@"正在獲取最新位置...", @"HUD loading title");
});
semaphore = dispatch_semaphore_create(0);
//信號量減1盯桦,如果>0,則向下執(zhí)行渤刃,否則等待
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
[hud hideAnimated:YES];
});
_locService.delegate=nil;
[_locService stopUserLocationService];
return _localXY;
}
//百度定位代理
- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation{
if (userLocation.location.coordinate.latitude!=0&&userLocation.location.coordinate.longitude!=0) {
_localXY=[NSString stringWithFormat:@"%f,%f",userLocation.location.coordinate.longitude,userLocation.location.coordinate.latitude];
//信號量加1
dispatch_semaphore_signal(semaphore);
}
}