美文网首页
Dispatch常用方法

Dispatch常用方法

作者: 哦小小树 | 来源:发表于2020-06-08 09:20 被阅读0次

dispatch_set_target_queue

  1. 变更Dispatch Queue的执行优先级
  2. 创建队列层次体系
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判断当前队列时判断为queueAqueueB队列。而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多线程和内存管理)/

相关文章

  • GCD中常用的方法

    主要是记录下GCD中常用的一些方法: dispatch_after dispatch group dispatch...

  • Dispatch常用方法

    dispatch_set_target_queue 变更Dispatch Queue的执行优先级 创建队列层次体系...

  • dispatch_time_t 的创建

    开发中常用到GCD方法dispatch_after(<#dispatch_time_t when#>, <#dis...

  • 多线程-GCD

    GCD常用API dispatch_queue_t 队列 dispatch_async 和 dispatch_sy...

  • iOS 使用dispatch_group_enter使多次网络请

    常用的几个方法 dispatch_group_enter :通知 group,下个任务要放入 group 中执行了...

  • GCD之dispatch

    异步dispatch_async同步dispatch_syn获取线程dispatch_get 常用的多线程 组队列...

  • GCD 详解

    常用的 dispatch_get_main_queue 主队列 dispatch_get_global_queu...

  • GCD(多线程)

    系统提供的dispatch方法 //后台执行: dispatch_async(dispatch_get_globa...

  • GCD

    1,GCD 栅栏方法:dispatch_barrier_async 2,GCD 延时执行方法:dispatch_a...

  • 延迟调用方法总结

    延迟调用自己代理的方法: //GCD延迟dispatch_after(dispatch_time(DISPATCH...

网友评论

      本文标题:Dispatch常用方法

      本文链接:https://www.haomeiwen.com/subject/aodnzhtx.html