iOS 快速复习GCD
多线程-串行、并行队列,同步、异步任务
1、创建串行队列和并行队列
//并行队列
dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
//串行队列
dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_SERIAL);
- 每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
- 可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务),并发队列 的并发功能只有在异步(dispatch_async)方法下才有效。
2、同步异步任务
//同步
dispatch_sync(queue, ^{
NSLog(@"1");
});
//异步
dispatch_async(queue, ^{
NSLog(@"1");
});
同步执行:
- 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
- 只能在当前线程中执行任务,不具备开启新线程的能力。
异步执行:
- 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
- 可以在新的线程中执行任务,具备开启新线程的能力。
异步执行(async) 虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关。
默认全局并发队列:dispatch_get_global_queue
第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT
。
第二个参数暂时没用,用 0
即可。
信号量 dispatch_semaphore_t
GCD
中的信号量dispatch_semaphore_t
中主要有三个函数:
dispatch_semaphore_create
:创建信号dispatch_semaphore_wait
:等待信号dispatch_semaphore_signal
:释放信号
1、dispatch_semaphore_create
参数为int,表示信号量初始值,需大于等于0,否则创建失败,返回一个dispatch_semaphore_t
2、dispatch_semaphore_wait
参数1:
需传递一个 dispatch_semaphore_t 类型对象,对信号进行减1,然后判断信号量大小
参数2:
传递一个超时时间:dispatch_time_t 对象
- 减1后信号量小于0,则阻塞当前线程,直到超时时间到达或者信号量大于等于0后继续执行后面代码
- 减1后信号量大于等于0,对dispatch_semaphore_t 进行赋值,并返回dispatch_semaphore_t对象,继续执行后面代码
3、dispatch_semaphore_signal
参数:dispatch_semaphore_t
进行信号量加1操作,如果加1后结果大于等于0,则继续执行,否则继续等待。
用法:
- (void)startAsync{
//创建信号量 值为0
self.sem = dispatch_semaphore_create(0);
//开启异步并发线程执行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"dispatch_semaphore 2\n");
sleep(5);
//发送信号,信号量值+1
dispatch_semaphore_signal(self.sem);
NSLog(@"dispatch_semaphore 3\n");
});
NSLog(@"dispatch_semaphore 0\n");
//信号量 值-1 小于0 等待信号。。。
dispatch_semaphore_wait(self.sem, DISPATCH_TIME_FOREVER);
NSLog(@"dispatch_semaphore 1\n");
}
执行顺序0 2 1 3 1和3不确定顺序
如果初始化创建是信号量值为1
执行顺序0 1 2 3
常用总结:
1、异步并发线程顺序执行
2、异步并发线程控制最大并发数,比如下载功能控制最大下载数
调度组 dispatch_group_t
主要API:
dispatch_group_create
:创建组dispatch_group_async
:进组任务dispatch_group_notify
:组任务执行完毕的通知dispatch_group_enter
:进组dispatch_group_leave
:出组dispatch_group_wait
:等待组任务时间
组合用法1:
- (void)dispatchGroupAsync{
//创建调度组
dispatch_group_t group = dispatch_group_create();
//获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//开启异步线程
dispatch_group_async(group, queue, ^{
sleep(2);
NSLog(@"11");
});
dispatch_group_async(group, queue, ^{
sleep(1);
NSLog(@"12");
});
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"13");
});
NSLog(@"14");
dispatch_group_notify(group, queue, ^{
//收到执行完成的通知后执行
NSLog(@"15");
});
//等待调度组执行完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
调度组执行完成后执行
NSLog(@"16");
}
用法2:
- (void)dispatchSyncEnterGroup{
//创建调度组
dispatch_group_t group = dispatch_group_create();
//获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//进入调度组
dispatch_group_enter(group);
//执行异步任务
dispatch_async(queue, ^{
sleep(2);
NSLog(@"21");
//执行完成后立刻调度组
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"22");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
sleep(3);
NSLog(@"23");
dispatch_group_leave(group);
});
NSLog(@"24");
dispatch_group_notify(group, queue, ^{
//执行完后回调
NSLog(@"25");
});
NSLog(@"26");
//等待调度组执行完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"27");
}
总结:
1、dispatch_group_async 是对dispatch_group_enter和dispatch_group_leave的封装
2、dispatch_group_enter和dispatch_group_leave的须成双成对的出现
事件源 dispatch_source_t
主要API:
dispatch_source_create
:创建源dispatch_source_set_event_handler
: 设置源的回调dispatch_source_merge_data
: 源事件设置数据dispatch_source_get_data
: 获取源事件的数据dispatch_resume
:恢复继续dispatch_suspend
:挂起uintptr_t dispatch_source_get_handle(dispatch_source_t source)
//得到dispatch源创建,即调用dispatch_source_create的第二个参数unsignedlong dispatch_source_get_mask(dispatch_source_t source)
; //得到dispatch源创建,即调用dispatch_source_create的第三个参数
源的类型dispatch_source_type_t
:
1. DISPATCH_SOURCE_TYPE_DATA_ADD:用于ADD合并数据
2. DISPATCH_SOURCE_TYPE_DATA_OR:用于按位或合并数据
3.DISPATCH_SOURCE_TYPE_DATA_REPLACE:跟踪通过调用dispatch_source_merge_data获得的数据的分派源,新获得的数据值将替换 尚未交付给源处理程序 的现有数据值
4. DISPATCH_SOURCE_TYPE_MACH_SEND:用于监视Mach端口的无效名称通知的调度源,只能发送没有接收权限
5. DISPATCH_SOURCE_TYPE_MACH_RECV:用于监视Mach端口的挂起消息
6. DISPATCH_SOURCE_TYPE_MEMORYPRESSURE:用于监控系统内存压力变化
7.DISPATCH_SOURCE_TYPE_PROC:用于监视外部进程的事件
8. DISPATCH_SOURCE_TYPE_READ:监视文件描述符以获取可读取的挂起字节的分派源
9. DISPATCH_SOURCE_TYPE_SIGNAL:监控当前进程以获取信号的调度源
10. DISPATCH_SOURCE_TYPE_TIMER:基于计时器提交事件处理程序块的分派源
11. DISPATCH_SOURCE_TYPE_VNODE:用于监视文件描述符中定义的事件的分派源
12. DISPATCH_SOURCE_TYPE_WRITE:监视文件描述符以获取可写入字节的可用缓冲区空间的分派源。
1、dispatch_source_create 参数:
dispatch_source_type_t
要创建的源类型uintptr_t
句柄 用于和其他事件并定,很少用,通常为0uintptr_t
mask 很少用,通常为0dispatch_queue_t
事件处理的调度队列
用法:
self.sourceAdd = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));
2、dispatch_source_set_event_handler 设置回调函数,当触发源事件时执行
//需要注意循环引用
dispatch_source_set_event_handler(self.sourceAdd, ^{
需要执行的代码
});
//启动
dispatch_resume(self.sourceAdd);
//挂起,即暂停
dispatch_suspend(self.sourceAdd);
这两个API需要成对使用,不可多次挂起或者多次恢复
3、dispatch_source_cancel 取消事件源,取消后不可再恢复或挂起,需要再次创建
4、dispatch_source_set_timer 当事件源类型为定时器类型(DISPATCH_SOURCE_TYPE_TIMER)时,设置开始时间、重复时间、允许时间误差
定时器实现比较简单容易,网上教程也多,这里主要介绍一下:DISPATCH_SOURCE_TYPE_DATA_ADD、DISPATCH_SOURCE_TYPE_DATA_OR、DISPATCH_SOURCE_TYPE_DATA_REPLACE。
先说下结果:
- DISPATCH_SOURCE_TYPE_DATA_ADD 会把事件源累加 可以记录总共发送多少次事件进行合并
- DISPATCH_SOURCE_TYPE_DATA_OR 会把事件源合并,最终得到的数据源数为1
- DISPATCH_SOURCE_TYPE_DATA_REPLACE 会用最新事件源替换旧有未处理事件,最终得到的数据源数为1
- 循环10000次实际跑处理回调事件次数 add315 or275 replace 284
从结果上来看,当需要把快速频繁的重复事件进行合并,最好的选择是DISPATCH_SOURCE_TYPE_DATA_OR
,使用场景,监听消息时,多消息频繁下发需要刷新UI,如果不进行合并处理,会导致UI太过频繁的刷新,影响最终效果,且对性能开销过大。
当然,类似的场景也可使用其他方式处理,比如建立消息池,接收消息后标记消息池状态及变化,然后定时从消息池中取消息。诸如此类的方法较多,如果只是简单的处理,上面的DISPATCH_SOURCE_TYPE_DATA_OR模式应该满足使用。
代码:
//创建源
self.sourceAdd = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));
//弱引用
__weak typeof(self) weakifySelf = self;
//设置回调事件
dispatch_source_set_event_handler(self.sourceAdd, ^{
//强引用
__strong typeof(self) strongSelf = weakifySelf;
//获取接收到的源数据
strongSelf.handleData = dispatch_source_get_data(strongSelf.sourceAdd);
NSLog(@"dispatch_source1 %ld\n",strongSelf.handleData);
//需要执行的代码
[strongSelf sourceHandle];
});
//开启源
dispatch_resume(self.sourceAdd);
for (int i = 0; i<10000; i ++) {
[self dispatchSource];
}
- (void)dispatchSource{
NSLog(@"dispatch_source2 %ld\n",self.handleData);
//发送源信号
dispatch_source_merge_data(self.sourceAdd, 1);
}
链接:https://juejin.cn/post/7261866427119222844
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。