Dispatch常用方法

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多线程和内存管理)/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351