GCD特殊函数使用
Posted WeaterMr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GCD特殊函数使用相关的知识,希望对你有一定的参考价值。
栅栏函数的使用
注意事项:
1.只针对于自己创建的并发队列起,如网络请求本身就在别的队列中在放到当前队列中,栅栏函数不起效。
2.如果用的是串行队列栅栏函数的作用等同于一个同步函数的作用,系统全局并发队列并不起效。
同步栅栏函数
dispatch_barrier_sync(在主线程中执行):必须等到栅栏之前的任务执行完毕,才执行栅栏中的任务,并且会堵塞栅栏下面的任务。
异步栅栏函数
dispatch_barrier_async:前面的任务执行完毕才会来到这里
- (void)demo2{
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_sync
dispatch_barrier_async(concurrentQueue, ^{
sleep(5);
NSLog(@"----栅栏%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
2021-08-12 11:20:42.644579+0800 [28005:4936975] 4
2021-08-12 11:20:42.644652+0800 [28005:4936993] 1
2021-08-12 11:20:42.644729+0800 [28005:4936989] 2
2021-08-12 11:20:47.650209+0800 [28005:4936989] 栅栏<NSThread: 0x2834e41c0>{number = 5, name = (null)}
2021-08-12 11:20:47.650806+0800 [28005:4936989] 3
通过打印结果可以总结,所谓的栅栏其实就相当于一张大的过滤网一样,并发队列就相当于一堵墙,当前面两个管道的水流完,在开启下两个管道的水。
这里为什么先执行4?
因为4就没有添加到当前的队列中,所以不起效。
获取系统的全局并发队列,栅栏不起效。
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
// 这里是可以的额!
/* 1.异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_sync
dispatch_barrier_async(concurrentQueue, ^{
sleep(5);
NSLog(@"----栅栏%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
2021-08-12 11:32:40.578252+0800 [28012:4940742] 4
2021-08-12 11:32:40.578325+0800 [28012:4940754] 1
2021-08-12 11:32:40.578402+0800 [28012:4940752] 2
2021-08-12 11:32:40.578518+0800 [28012:4940753] 3
2021-08-12 11:32:45.583531+0800 [28012:4940754] ----栅栏<NSThread: 0x283059700>{number = 6, name = (null)}-----
原因:
系统全局的并发队列可能对系统层次的资源进行调度,又因为,栅栏有类似堵塞的作用,所以会失效。
同步栅栏函数堵塞线程
- (void)demo2{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
// 这里是可以的额!
/* 1.异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_sync
dispatch_barrier_sync(concurrentQueue, ^{
sleep(5);
NSLog(@"----栅栏%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
2021-08-12 13:36:39.012807+0800 [28226:4973408] 2
2021-08-12 13:36:39.012804+0800 [28226:4973411] 1
2021-08-12 13:36:44.014448+0800 [28226:4973286] ----栅栏<NSThread: 0x281174980>{number = 1, name = main}-----
2021-08-12 13:36:44.015104+0800 [28226:4973286] 4
2021-08-12 13:36:44.015175+0800 [28226:4973407] 3
总结:这个堵塞将影响到不再当前队列中的任务。
可变线程安全问题
- (void)demo3{
// 可变数组线程安全?
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
// 多线程 操作marray
for (int i = 0; i<1000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
}
}
解决方案:添加栅栏函数或互斥锁
dispatch_barrier_async(concurrentQueue , ^{
[self.mArray addObject:image];
});
@synchronized (self) {
[self.mArray addObject:image];
};
信号量 针对并发队列
信号量的作用一般是用来使任务同步执行,类似于互斥锁,用户可以根据需要控制GCD最大并发数,来控制最大能同时执行多少任务。
基本使用
//信号量
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(sem);
- 这里的
sem
即最大开辟线程数量,最多可以执行多少个任务。 - dispatch_semaphore_create 主要就是初始化信号量
- dispatch_semaphore_wait是对信号量的value进行–,即加锁操作
- dispatch_semaphore_signal 是对信号量的value进行++,即解锁操作
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("111", DISPATCH_QUEUE_CONCURRENT);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
NSLog(@"执行任务1");
NSLog(@"任务1完成");
});
//任务2
dispatch_async(queue, ^{
sleep(2);
NSLog(@"执行任务2");
NSLog(@"任务2完成");
dispatch_semaphore_signal(sem); // 发信号
});
2021-08-12 17:12:39.931461+0800 006---GCD最大并发数[28746:5069779] 执行任务2
2021-08-12 17:12:39.931967+0800 006---GCD最大并发数[28746:5069779] 任务2完成
2021-08-12 17:12:39.932239+0800 006---GCD最大并发数[28746:5069780] 执行任务1
2021-08-12 17:12:39.932483+0800 006---GCD最大并发数[28746:5069780] 任务1完成
调度组
dispatch_group_create 创建组
dispatch_group_async 进组任务
dispatch_group_notify 进组任务执行完毕通知 dispatch_group_wait 进组任务执行等待时间
`常规用法`
```objectivec
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
// group 负责监控任务,queue 负责调度任务
dispatch_group_async(group, q, ^{
sleep(1);
NSLog(@"任务1 %@", [NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
NSLog(@"任务2 %@", [NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
NSLog(@"任务3 %@", [NSThread currentThread]);
});
// 监听所有任务完成 - 等到 group 中的所有任务执行完毕后,"由队列调度 block 中的任务异步执行"
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 修改为主队列,后台批量下载,结束后,主线程统一更新UI
NSLog(@"OK %@", [NSThread currentThread]);
});
NSLog(@"come here");
GCD最大并发数[30010:5267216] come here
GCD最大并发数[30010:5267353] 任务3 <NSThread: 0x283d95440>{number = 3, name = (null)}
GCD最大并发数[30010:5267349] 任务2 <NSThread: 0x283dca240>{number = 6, name = (null)}
GCD最大并发数[30010:5267350] 任务1 <NSThread: 0x283dda240>{number = 5, name = (null)}
GCD最大并发数[30010:5267216] OK <NSThread: 0x283d90980>{number = 1, name = main}
进组和出组一般是成对使用的
dispatch_group_enter 进组
dispatch_group_leave 出组
```objectivec
// 群组-统一监控一组任务
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
// 1> 入组 -> 之后的 block 会被 group 监听
// dispatch_group_enter 一定和 dispatch_group_leave 要配对出现
dispatch_group_enter(group);
dispatch_async(q, ^{
NSLog(@"task1 %@", [NSThread currentThread]);
// block 的末尾,所有任务执行完毕后,添加一个出组
dispatch_group_leave(group);
});
// 再次入组
dispatch_group_enter(group);
dispatch_async(q, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"task2 %@", [NSThread currentThread]);
// block 的末尾,所有任务执行完毕后,添加一个出组
dispatch_group_leave(group);
});
// 群组结束
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"OVER");
});
NSLog(@"come here");
dispatch_source 使用
概念
dispatch_source
是基础数据类型,用于协调特定底层系统事件的处理
dispatch_source
替代了异步回调函数,来处理系统相关的事件,当配置一个dispatch时,你需要指定监测的事件、dispatch queue、以及处理事件的代码(block或函数)。当事件发生时,dispatch source会提交你的block或函数到指定的queue去执行
简单来说:这种事件是由你调用 dispatch_source_merge_data 函数来向自己发出的信号。
句柄
是一种指向指针的指针,它指向的就是一个类或者结构,它和系统有密切的关系,这当中还有一个通用的句柄,就是HANDLE
实例句柄 HINSTANCE
位图句柄 HBITMAP
设备表句柄 HDC
图标句柄 HICON
特点优势
其CPU负荷非常小,基本不占用资源,联结的优势
dispatch_source_t source = dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)
案例
//倒计时时间
__block int timeout = 5;
//创建队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
//创建timer
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, globalQueue);
//设置1s触发一次,0s的误差
/*
- source 分派源
- start 数控制计时器第一次触发的时刻。参数类型是 dispatch_time_t,这是一个opaque类型,我们不能直接操作它。我们得需要 dispatch_time 和 dispatch_walltime 函数来创建它们。另外,常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用。
- interval 间隔时间
- leeway 计时器触发的精准程度
*/
dispatch_source_set_timer(timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0);
//触发的事件
dispatch_source_set_event_handler(timer, ^{
//倒计时结束,关闭
if (timeout <= 0) {
//取消dispatch源
dispatch_source_cancel(timer);
}else{
timeout--;
dispatch_async(dispatch_get_main_queue(), ^{
//更新主界面的操作
NSLog(@"倒计时 - %d", timeout);
});
}
});
//开始执行dispatch源
dispatch_resume(timer);
以上是关于GCD特殊函数使用的主要内容,如果未能解决你的问题,请参考以下文章