dispatch_set_target_queue
- 变更Dispatch Queue的执行优先级
- 创建队列层次体系
queueA = dispatch_queue_create("com.lyk.queueA", NULL);
queueB = dispatch_queue_create("com.lyk.queueB", DISPATCH_QUEUE_CONCURRENT);
// 将queueB队列中的任务依次添加到queueA中。这样queueB的优先级就与queueA保持一致了。
// 而且设置了target后,在判断当前任务执行队列会出现点事情
dispatch_set_target_queue(queueB, queueA);
延伸逻辑
创建队列层次体系,是什么样的层次体系?先看一个例子
// 准备工作
static void * queueuBKey = &queueuBKey;
queueA = dispatch_queue_create("com.lyk.queueA", NULL);
queueB = dispatch_queue_create("com.lyk.queueB", DISPATCH_QUEUE_CONCURRENT);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// dispatch_queue_set_specific(queueA, queueuAKey, queueuAKey, NULL);
dispatch_queue_set_specific(queueB, queueuBKey, queueuBKey, NULL);
});
// 测试
dispatch_set_target_queue(queueA, queueB);
dispatch_sync(queueB, ^{
NSLog(@"queueB-是否为B:%@", dispatch_get_specific(queueuBKey) == queueuBKey ? @"True" : @"False");
});
dispatch_sync(queueA, ^{
NSLog(@"queueA-是否为B:%@", dispatch_get_specific(queueuBKey) == queueuBKey ? @"True" : @"False");
});
/* 打印输出
* queueB-是否为B:True
* queueA-是否为B:True
*/
// 如果设置成这样
dispatch_set_target_queue(queueB, queueA);
/* 打印输出
* queueB-是否为B:True
* queueA-是否为B:False
*/
使用dispatch_get_specific
来验证当前队列,我们看下其内部实现原理:
void * dispatch_get_specific(const void *key)
{
if (slowpath(!key)) {
return NULL;
}
void *ctxt = NULL;
dispatch_queue_t dq = _dispatch_queue_get_current();
while (slowpath(dq)) {
if (slowpath(dq->dq_specific_q)) {
ctxt = (void *)key;
dispatch_sync_f(dq->dq_specific_q, &ctxt,
_dispatch_queue_get_specific);
if (ctxt) break;
}
dq = dq->do_targetq; // 如果当前队列找不到标识,就会根据其targetq向上找
}
return ctxt;
}
通过上例可以发现,上述打印效果区分:
// 相当于queueB的targetq是queueA
dispatch_set_target_queue(queueB, queueA);
/*以下展示目标关系,B.do_targetq = A; A 在上游,B在下游
A NULL ; 当在A队列中执行以下代码获取到 NULL ,向上游找targetq结果还是找不到
B queueuBKey; 当在B队列中执行以下代码会获取到 queueuBKey
*/
dispatch_get_specific(queueuBKey) // queueA,判断不为B; queueB判断为B
// 相当于queueA的targetq是queueB
dispatch_set_target_queue(queueA, queueB);
/*以下展示目标关系,A.do_targetq = B; B 在上游,A在下游
B queueuBKey; 当在B队列中执行以下代码会获取到 queueuBKey
A NULL ; 当在A队列中获取为NULL,需要顺着targetq向上游找,就找到B,进而找到queueBKey
*/
dispatch_get_specific(queueuBKey); // queueA,QueueB都可以判断为B队列
总结来说:dispatch_set_target_queue(queueA, queueB);
会将queueA
队列中的任务提交到queueB中执行
;
所以在queueA
中的任务在执行过程中通过dispatch_get_specific
判断当前队列时判断为queueA
或queueB
队列。而queueB
中的任务在执行中判断当前队列时只能判断为queueB
。
查找当前队列,如果查找不到会顺着do_targetq向上游查找。
dispatch_after
在3s
后将任务块添加到MainDispatchQueue
中;注意不是在指定时间后执行处理,只是在指定的时间追加操作到指定队列中。
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at least three seconds.");
});
dispatch_group
用来创建任务组,当任务组执行完毕后再去执行其他任务
常用方法:
/*永久等待:第二个参数指定为等待的时间(超时),此处意味着永久等待。
只要属于 Dispatch Group 的处理尚未执行结束,就会一直等待,中途不能取消*/
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
/*等待指定时间: 注意超时时间的入参*/
long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC));
NSLog(@"是否执行超时:%@",timeout == 0 ? @"没有超时":[NSString stringWithFormat:@"超时%.2fs",timeout/1000.0f]);
/*用来监听队列中的任务已全部执行完毕,任务块将会放到queue队列中执行*/
dispatch_group_notify(group, queue, ^{NSLog(@"done");});
使用方式
以下两种方式使用起来效果一致,但是如果block
内部又涉及到异步队列操作问题,可能会碰到第一种方法解决不了的情况,此时就需要考虑使用第二种方式来保证顺序了。
1. 通过调度组关联队列的方式,将一个block添加到队列中并且与一个组进行关联,当任务执行完毕
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
2. 通过自己嵌入队列中,需要注意是手动关联队列,enter与leave必须要保持平衡;而且可以关联多个组
dispatch_group_enter(group);
dispatch_async(queue1, ^{
NSLog(@"测试4,%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
block
内部涉及队列导致group
保证不了顺序的情况
需要注意,当我们通过dispatch_group_async
添加一个任务指的是将任务添加到队列,同时关联组,当执行完毕这个任务就通知调度组,它并去去管你这个队列内部做了什么操作(是去执行任务,还是将任务添加到其他队列)。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("con.song.serial", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
// dispatch_group_enter(group); // 打开注释可以解决notify通知不到的问题
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 执行一些任务块
sleep(3);
NSLog(@"测试1,%@",[NSThread currentThread]);
// dispatch_group_leave(group);
});
});
dispatch_group_async(group, queue, ^{
// 执行一些任务块
NSLog(@"测试2,%@",[NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"执行任务结束:%@",[NSThread currentThread]);
});
// 打印结果(notify不是最后打印的)
QueueTest[51292:1686388] 测试2,<NSThread: 0x600003e8f980>{number = 6, name = (null)}
QueueTest[51292:1686192] 执行任务结束:<NSThread: 0x600003ec4f00>{number = 1, name = main}
QueueTest[51292:1686392] 测试1,<NSThread: 0x600003e8ed40>{number = 3, name = (null)}
dispatch_barrier_async
只能用自己创建的并行队列来操作
添加栅栏操作后,会等待已经追加到队列中的block全部执行完毕。
dispatch_sync
同步执行任务,如果在串行队列中执行容易导致死锁问题,注意处理。
dispatch_apply
是dispatch_sync函数和dispatch_group的关联的API。该函数按照指定的次数将指定的block追加到指定的dispatch_queue中,病等待全部操作执行结束。
dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(3, queue, ^(size_t index) {
NSLog(@"%zu", index);
});
NSLog(@"done");
dispatch_suspendispatch
/ dispatch_resume
挂起指定的队列/恢复指定的队列。
这些函数对已经执行的处理没有影响。
挂起后,追加到Dispatch Queue中但尚未执行的操作在此之后停止执行。
而恢复则使得这些处理能继续执行。
dispatch_semaphore
持有计数的信号,该计数是多线程中的计数类型信号。
可以用来做任务同步,比如讲异步网络请求通过这种方案设计为同步请求操作。
// 创建一个信号 v = 1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 执行一个任务会将信号量 -1; 注意如果信号量<1此时会进入等待,可以设置等待超时时间
// 如果返回值为0说明正常操作,如果返回值不为0则代表超时时间
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 释放一个信号会将信号量 +1
dispatch_semaphore_signal(semaphore);
dispatch_once
保证应用程序执行中只执行一次指定的逻辑
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 初始化操作
});
dispatchI/O
适用场景:适合文章类的读取操作(PDF,md,work,txt),因为串行队列,所以读取的文章是顺序的,在实际中使用更多。
参考:
https://www.jianshu.com/p/33d6f52fe26b
[https://raykle.github.io/2016/08/16/《iOS%20与%20OS%20X%20多线程和内存管理》读书笔记之%20GCD(二)/](iOS与OS X多线程和内存管理)/