“这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战”
一.栅栏函数的应用
1.并发栅栏函数异步
- (void)concurrent_barrierAsync
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
/* 1.异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_async
dispatch_barrier_async(concurrentQueue, ^{
NSLog(@"----%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
运行打印结果
2.并发栅栏函数同步
- (void)concurrent_barrierSync
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
/* 1.异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_sync
dispatch_barrier_sync(concurrentQueue, ^{
NSLog(@"----%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
运行打印结果
3.全局队列栅栏函数异步
- (void)global_barrierAsync
{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
/* 1.异步函数 */
dispatch_async(concurrentQueue, ^{
sleep(1.5);
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
sleep(1);
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_async
dispatch_barrier_async(concurrentQueue, ^{
NSLog(@"----%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
sleep(0.5);
NSLog(@"3");
});
// 4
NSLog(@"4");
}
运行打印结果 并发无序 不可使用全局队列
4.全局队列栅栏函数同步
- (void)global_barrierSync
{
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, ^{
NSLog(@"----%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
运行打印结果 并发1 2执行完 [NSThread currentThread] 4 3
5.串行栅栏函数异步
- (void)serial_barrierAsync
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
/* 1.异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_async
dispatch_barrier_async(concurrentQueue, ^{
NSLog(@"----%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
运行打印结果 4无序 1 2 [NSThread currentThread] 3有序
6.串行栅栏函数同步
- (void)serial_barrierSync
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
/* 1.异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
/* 2. 栅栏函数 */ // - dispatch_barrier_sync
dispatch_barrier_sync(concurrentQueue, ^{
NSLog(@"----%@-----",[NSThread currentThread]);
});
/* 3. 异步函数 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
运行打印结果
二.栅栏函数的底层原理
为什么栅栏函数可以堵塞下面的代码 并且控制流程 下面我们探究一下原理 为什么可以使用自定义并发队列 而不能使用全局队列
1.dispatch_barrier_sync
进入 _dispatch_barrier_sync_f
进入 _dispatch_barrier_sync_f_inline
先进入_dispatch_sync_recurse
要想dispatch_barrier_sync的block回调 必须的把dispatch_barrier_sync前面的任务全部执行完 在执行block 意味着当前队列无任务
接下来我们看_dispatch_sync_invoke_and_complete_recurse
接下来我们进入 _dispatch_sync_complete_recurse
进入dx_wakeup->dq_wakeup
串行和并发
dq_wakeup->_dispatch_lane_wakeup 全局队列dq_wakeup->_dispatch_root_queue_wakeup
进入 _dispatch_lane_wakeup
进入
_dispatch_lane_barrier_complete 通过_dispatch_lane_drain_non_barriers这个把barriers干掉
进入 _dispatch_lane_class_barrier_complete 所有状态清空
全局队列dq_wakeup->_dispatch_root_queue_wakeup 这里面没有对栅栏处理
2.dispatch_barrier_async
进入
_dispatch_continuation_async
进入dx_push->dq_push
并发进入 _dispatch_lane_concurrent_push
是
barrier 进入 _dispatch_lane_push
然后走同步的流程 进入
dx_wakeup->dq_wakeup
三.信号量使用
- dispatch_semaphore_create 创建信号量
- dispatch_semaphore_wait 信号量等待
- dispatch_semaphore_signal 信号量释放
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
NSLog(@"执行任务1");
NSLog(@"任务1完成");
dispatch_semaphore_signal(sem); // 发信号
});
//任务2
dispatch_async(queue, ^{
sleep(2);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
NSLog(@"执行任务2");
NSLog(@"任务2完成");
dispatch_semaphore_signal(sem); // 发信号
});
//任务3
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
sleep(1);
NSLog(@"执行任务3");
NSLog(@"任务3完成");
dispatch_semaphore_signal(sem);
});
打印结果
2021-08-14 17:13:51.790864+0800 006---GCD最大并发数[37192:1201311] 执行任务1
2021-08-14 17:13:51.791029+0800 006---GCD最大并发数[37192:1201311] 任务1完成
2021-08-14 17:13:52.793637+0800 006---GCD最大并发数[37192:1201313] 执行任务3
2021-08-14 17:13:52.794132+0800 006---GCD最大并发数[37192:1201313] 任务3完成
2021-08-14 17:13:53.794370+0800 006---GCD最大并发数[37192:1201309] 执行任务2
2021-08-14 17:13:53.794940+0800 006---GCD最大并发数[37192:1201309] 任务2完成
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(2);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
sleep(0.5);
NSLog(@"执行任务1");
NSLog(@"任务1完成");
dispatch_semaphore_signal(sem); // 发信号
});
//任务2
dispatch_async(queue, ^{
sleep(0.5);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
NSLog(@"执行任务2");
NSLog(@"任务2完成");
dispatch_semaphore_signal(sem); // 发信号
});
//任务3
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
sleep(0.5);
NSLog(@"执行任务3");
NSLog(@"任务3完成");
dispatch_semaphore_signal(sem);
});
//任务4
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
sleep(0.5);
NSLog(@"执行任务4");
NSLog(@"任务4完成");
dispatch_semaphore_signal(sem);
});
打印结果
2021-08-14 17:16:37.592382+0800 006---GCD最大并发数[37213:1203700] 执行任务1
2021-08-14 17:16:37.592394+0800 006---GCD最大并发数[37213:1203701] 执行任务3
2021-08-14 17:16:37.592519+0800 006---GCD最大并发数[37213:1203701] 任务3完成
2021-08-14 17:16:37.592519+0800 006---GCD最大并发数[37213:1203700] 任务1完成
2021-08-14 17:16:37.592673+0800 006---GCD最大并发数[37213:1203702] 执行任务2
2021-08-14 17:16:37.592699+0800 006---GCD最大并发数[37213:1203704] 执行任务4
2021-08-14 17:16:37.592799+0800 006---GCD最大并发数[37213:1203702] 任务2完成
2021-08-14 17:16:37.592839+0800 006---GCD最大并发数[37213:1203704] 任务4完成
dispatch_semaphore_create控制最大并发数
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
sleep(3);
NSLog(@"执行任务1");
NSLog(@"任务1完成");
});
dispatch_async(queue, ^{
sleep(3);
NSLog(@"执行任务2");
NSLog(@"任务2完成");
dispatch_semaphore_signal(sem); // 发信号
});
打印结果
2021-08-14 20:58:18.699219+0800 006---GCD最大并发数[37635:1299035] 执行任务2
2021-08-14 20:58:18.699519+0800 006---GCD最大并发数[37635:1299035] 任务2完成
2021-08-14 20:58:21.700031+0800 006---GCD最大并发数[37635:1299030] 执行任务1
2021-08-14 20:58:21.700493+0800 006---GCD最大并发数[37635:1299030] 任务1完成
dispatch_semaphore_wait等待 执行dispatch_semaphore_signal发信号才能执行任务1 达到一个控制流程的效果
四.信号量原理
dispatch_semaphore_wait等待 dispatch_semaphore_signal发信号 底层到底做了什么事情
1.dispatch_semaphore_wait
如果创建的
dispatch_semaphore_t sem = dispatch_semaphore_create(0)
0-1 = -1 直接走 _dispatch_semaphore_wait_slow
进入
_dispatch_sema4_wait
ret == -1 就一直dowhile循环卡死这里
所以应该先执行另外的线程 先执行dispatch_semaphore_signal 在执行dispatch_semaphore_wait
如果创建的dispatch_semaphore_t sem = dispatch_semaphore_create(0)
0+1 =1 直接return 然后执行dispatch_semaphore_wait 1-1 = 0 直接return
五.调度组的应用问题
dispatch_group_create创建组dispatch_group_async进组任务dispatch_group_notify进组任务执行完毕通知dispatch_group_wait进组任务执行等待时间dispatch_group_enter进组dispatch_group_leave出组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
//创建调度组
[self.mArray addObject:@"1"];
});
dispatch_group_async(group, queue, ^{
[self.mArray addObject:@"2"];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"数组=%@",self.mArray);
});
打印结果
2021-08-14 22:37:22.112900+0800 005---GCD进阶使用(下)[38331:1372252] 数组=(
1,
2
)
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
//创建调度组
[self.mArray addObject:@"1"];
});
dispatch_group_async(group, queue, ^{
[self.mArray addObject:@"2"];
});
// 进组和出组 成对 先进后出
dispatch_group_enter(group);
dispatch_async(queue, ^{
[self.mArray addObject:@"3"];
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"数组=%@",self.mArray);
});
打印结果
2021-08-14 22:40:15.258917+0800 005---GCD进阶使用(下)[38364:1375249] 数组=(
1,
2,
3
)
六.调度组的原理
- 进组和出组顺序颠倒为啥会崩溃
- 为什么有同步的效果
dispatch_group_async = dispatch_group_enter + dispatch_group_leave
1.dispatch_group_create
进入 _dispatch_group_create_with_count 跟信号量的初始化不同
2.dispatch_group_enter
0->-1
3.dispatch_group_leave
-1->0 唤醒dispatch_group_notify继续执行
4.dispatch_group_async
进入
_dispatch_continuation_group_async
进入
_dispatch_continuation_async
进入dx_push->dq_push
进入_dispatch_root_queue_push
进入
_dispatch_root_queue_push_inline
进入_dispatch_root_queue_poke
进入
_dispatch_root_queue_poke_slow
进入
_dispatch_root_queues_init
进入
_dispatch_root_queues_init_once
进入 _dispatch_worker_thread2
进入 _dispatch_root_queue_drain
进入_dispatch_continuation_pop_inline
进入 _dispatch_continuation_invoke_inline
进入
_dispatch_continuation_with_group_invoke
七.dispatch_source
dispatch_source_create创建源dispatch_source_set_event_handler设置源回调事件dispatch_source_merge_data源事件设置数据dispatch_source_get_data获取事件源数据dispatch_resume继续dispatch_suspend挂起
__block int timeout=10; //倒计时时间
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(self.timer ,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(self.timer, ^{
if(timeout<=0){ //倒计时结束,关闭
dispatch_source_cancel(self.timer);
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置
NSLog(@"timer 停止");
});
}else{
int minutes = timeout / 60;
int seconds = timeout % 60;
NSString *strTime = [NSString stringWithFormat:@"%d分%.2d秒后重新获取验证码",minutes, seconds];
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置
NSLog(@"%@",strTime);
});
timeout--;
}
});
//恢复 启动
dispatch_resume(self.timer);
//挂起
dispatch_suspend(self.timer);
八.可变数组不安全分析
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", 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];
});
}
运行打印结果发现崩溃 原因是线程不安全 self.mArray不断的读写 写的操作是把旧值release 新值retain 有可能一个线程正在release 另外一个线程正在读 读取的是野指针 同一时间对同一片内存空间进行操作 不安全 为了让其安全需要加锁 或者加dispatch_barrier_async
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", 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];
dispatch_barrier_async(concurrentQueue , ^{
[self.mArray addObject:image];
});
});
}