多线程-串行、并行队列,同步、异步任务
1、创建串行队列和并行队列
arduino
//并行队列
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、同步异步任务
objectivec
//同步
dispatch_sync(queue, ^{
NSLog(@"1");
});
//异步
dispatch_async(queue, ^{
NSLog(@"1");
});
同步执行:
- 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
- 只能在当前线程中执行任务,不具备开启新线程的能力。
异步执行:
- 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
- 可以在新的线程中执行任务,具备开启新线程的能力。
异步执行(async) 虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关。
默认全局并发队列:dispatch_get_global_queue
第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT
。
第二个参数暂时没用,用 0
即可。
区别 | 并发队列 | 串行队列 | 主队列 |
---|---|---|---|
同步(sync) | 没有开启新线程,串行执行任务 | 没有开启新线程串行执行任务 | 死锁卡住不执行 |
异步(async) | 有开启新线程,并发执行任务 | 有开启新线程(1条)串行执行任务 | 没有开启新线程串行执行任务 |
信号量 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,则继续执行,否则继续等待。
用法:
scss
- (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:
scss
- (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:
scss
- (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
:
-
DISPATCH_SOURCE_TYPE_DATA_ADD
:用于ADD
合并数据
-
DISPATCH_SOURCE_TYPE_DATA_OR
:用于按位或
合并数据
-
DISPATCH_SOURCE_TYPE_DATA_REPLACE
:跟踪
通过调用dispatch_source_merge_data
获得的数据的分派源,新获得的数据值将替换
尚未交付给源处理程序 的现有数据值
-
DISPATCH_SOURCE_TYPE_MACH_SEND
:用于监视Mach端口
的无效名称
通知的调度源,只能发送没有接收权限
-
DISPATCH_SOURCE_TYPE_MACH_RECV
:用于监视Mach端口
的挂起
消息
-
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE
:用于监控
系统内存压力变化
-
DISPATCH_SOURCE_TYPE_PROC
:用于监视外部进程
的事件
-
DISPATCH_SOURCE_TYPE_READ
:监视
文件描述符以获取可读取的挂起字节
的分派源
-
DISPATCH_SOURCE_TYPE_SIGNAL
:监控当前进程
以获取信号的调度源
-
DISPATCH_SOURCE_TYPE_TIMER
:基于计时器提交事件
处理程序块的分派源
-
DISPATCH_SOURCE_TYPE_VNODE
:用于监视
文件描述符中定义的事件
的分派源
-
DISPATCH_SOURCE_TYPE_WRITE
:监视
文件描述符以获取可写入字节
的可用缓冲区空间的分派源。
1、dispatch_source_create 参数:
dispatch_source_type_t
要创建的源类型uintptr_t
句柄 用于和其他事件并定,很少用,通常为0uintptr_t
mask 很少用,通常为0dispatch_queue_t
事件处理的调度队列
用法:
ini
self.sourceAdd = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));
2、dispatch_source_set_event_handler 设置回调函数,当触发源事件时执行
scss
//需要注意循环引用
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模式应该满足使用。
代码:
scss
//创建源
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);
}