iOS多线程之GCD

主要涉及到的概念git

  • 任务:放在block中的代码,执行方式有 同步执行(sync)异步执行(async)
  • 队列: 串行队列(serial)并发队列(concurrent)
    • 主队列:本质是一个串行队列
    • 全局队列:本质是一个并发队列

知识点:程序员

  • 主队列的任务必定在主线程执行,主线程能够执行非主队列的任务
  • 同步执行不开辟子线程,在当前线程执行
  • 异步执行具备开辟子线程的能力,可是不必定会开辟,后面会有例子
  • 同步执行串行队列按照顺序串行执行
  • 同步执行并发队列依然是按照顺序串行执行
  • 异步执行串行队列会开辟一个子线程,按顺序执行队列任务
  • 异步执行并发队列具有开辟多个子线程的能力

任务的执行方式有两种,队列有四种,共有8种组合,分别为:github

同步执行+串行队列

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
    
    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueSerial, task);
    dispatch_sync(queueSerial, task1);
    NSLog(@"end");
复制代码

当前是在主线程运行,同步执行会阻塞当前线程(主线程),因此end会在执行完tasktask1以后输出,安全

dispatch_sync(queueSerial, task);
复制代码
  • 同步执行dispatch_sync阻塞当前线程(主线程)
  • 向串行队列queueSerial中插入任务task
  • 在当前线程(主线程)执行task任务
  • task任务执行完成解除对当前线程(主线程)的阻塞
dispatch_sync(queueSerial, task1); //同理
复制代码
  • 同步执行dispatch_sync阻塞当前线程(主线程)
  • 向串行队列queueSerial中插入任务task1
  • 在当前线程(主线程)执行task1任务
  • task1任务执行完成解除对当前线程(主线程)的阻塞

因此执行顺序为task->task1->endmarkdown

image.png

同步执行+并发队列

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueConcurrent, task);
    dispatch_sync(queueConcurrent, task1);
    NSLog(@"end");
复制代码

在当前线程同步执行不管是串行队列仍是并发队列任务都是按照顺序执行,其结果和 同步执行+串行队列 一致多线程

同步执行全局队列

//第一个参数是队列优先级,第二个参数是保留值默认传0
    dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueGlobal, task);
    dispatch_sync(queueGlobal, task1);
    NSLog(@"end");
复制代码

全局队列本质是并发队列,其结果和 同步执行+并发队列 一致并发

同步执行主队列

dispatch_queue_t queueMain = dispatch_get_main_queue();

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueMain, task); //我也是一个任务 记为 sync1
    dispatch_sync(queueMain, task1);
    NSLog(@"end");
复制代码

这种状况比较特殊,会引发死锁,其缘由是同步执行语句dispatch_sync(queueMain, task);自己也是一个任务,咱们记为 sync1sync1任务是在当前线程(主线程)同步执行的,sync1任务是向主队列中添加一个任务task并在当前线程(主线程)同步执行任务task,也就是说sync1任务执行完成的前提是task任务执行完成。 由于task任务是在sync1任务以后插入主队列的,因此主线程要先执行完sync1任务才会去执行task任务,而sync1任务执行完成的前提是task任务执行完成,因此就陷入了死锁状态,形成崩溃。app

image.png

相似的异步

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
    void(^task1)(void) = ^{
        NSLog(@"死锁了吗");
    };

    void(^task2)(void) = ^{
        dispatch_sync(queueSerial, task1);
    };

    //开辟子线程,并向串行队列queueSerial中添加任务 task2
    dispatch_async(queueSerial, task2);
复制代码

dispatch_async(queueSerial, task2);自己也是一个任务记为sync1,但不一样的是sync1是在当前线程(主线程)执行,sync1任务执行结果是向串行队列queueSerial中添加任务task2并开辟子线程执行task2,任务task2是向串行队列queueSerial中添加任务task1并在当前线程同步执行任务task1,也就是说task2任务执行完成的前提是task1任务执行完成,由于task2任务比task1任务先添加到串行队列queueSerial,因此任务task2执行完成以后才会去执行tasi1async

注意:异步执行+串行队列只会开辟一条子线程

image.png

异步执行串行队列

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueSerial, task); //我也是一个任务 记为 sync1
    dispatch_async(queueSerial, task1);
    NSLog(@"end");
复制代码

tasktask1分别添加到串行队列queueSerial,开辟一条子线程顺序执行队列queueSerial中的任务,由于end是在主线程输出的,因此输出顺序为end->task->task1,由于异步执行+串行队列只开辟一条子线程,因此tasktask1在同一个线程中执行

image.png

异步执行并发队列

这个时候理论上能够开辟多个子线程了

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueConcurrent, task);
    dispatch_async(queueConcurrent, task1);
    NSLog(@"end");
复制代码

由于tasktask1有可能不是在同一个字线程执行的,两个任务不必定谁先结束执行,因此不能肯定tasktask1谁先输出,由于end是在主线程输出的,因此先输出end

异步执行主队列

dispatch_queue_t queueMain = dispatch_get_main_queue();

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueMain, task);
    dispatch_async(queueMain, task1);
复制代码

主队列本质上也是一个串行队列,因此按照顺序执行task->task1,另外祝队列任务只能在主线程执行,因此tasktask1都在主线程执行

异步执行全局队列

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueGlobal, task);
    dispatch_async(queueGlobal, task1);
复制代码

全局队列是一个并发队列,因此有能力开辟多个子线程,固然也可能在一个子线程中去执行

image.png

线程间通信

咱们须要将一些耗时任务放在子线程,执行完毕以后再回到主线程刷新页面

dispatch_queue_t queueMain = dispatch_get_main_queue();
    dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);
    dispatch_async(queueGlobal, ^{
        sleep(3);//模拟耗时操做
        dispatch_async(queueMain, ^{
            NSLog(@"主线程");
        });

    });
复制代码

GCD 栅栏方法:dispatch_barrier_async

在《程序员的自我修养:连接、装载与库。》一书的过分优化部分有这么一段话

“CPU的乱序执行能力让咱们对多线程的安全保障的努力变得异常困难。所以要保证线程安全,阻止CPU换序是必需的。遗憾的是,如今并不存在可移植的阻止换序的方法。一般状况下是调用CPU提供的一条指令,这条指令经常被称为barrier。一条barrier指令会阻止CPU将该指令以前的指令交换到barrier以后,反之亦然。换句话说,barrier指令的做用相似于一个拦水坝,阻止换序“穿透”这个大坝。”

摘录来自: 俞甲子 石凡 潘爱民. “程序员的自我修养:连接、装载与库。” Apple Books.

为了保证某些操做的原子性,CUP提供了barrier指令,用来保证在barrier指令以前的指令执行完成以后才会执行barrier以后的指令,dispatch_barrier_async的意思大致也是如此,在异步执行并发队列中保证先执行完dispatch_barrier_async以前的任务,而后再执行dispatch_barrier_async中的任务,其次执行dispatch_barrier_async以后的任务

image.png

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_async(queueGlobal, ^{
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_async(queueGlobal, ^{
        sleep(2);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_barrier_async(queueGlobal, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
    });

    dispatch_async(queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_async(queueGlobal, ^{
        NSLog(@"4--%@",[NSThread currentThread]);
    });
复制代码

全局队列是一个并发队列,盲猜这里应该先打印出来12,而后执行barrier,而后随机打出34,可是实际上呢

image.png

barrier彷佛跟dispatch_async同样,并无起到什么做用,查了一番资料终于在官方文档找到正解 dispatch​_barrier​_async

image.png

若是本身经过dispatch_queue_create建立并发队列没问题,若是是一个串行队列或者全局并发队列那么和dispatch_async效果同样同样的,咱们换成本身建立的并发队列

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queueConcurrent, ^{
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        sleep(2);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_barrier_async(queueConcurrent, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"4--%@",[NSThread currentThread]);
    });
    NSLog(@"end");
复制代码

image.png

从结果看到dispatch_barrier_async并无阻塞当前线程,而只是阻塞了当前异步队列其余任务的执行,官方文档也证明了这一点

image.png

dispatch_barrier_sync

咱们先去官方文档看一下dispatch_barrier_sync

image.png

一方面dispatch_barrier_sync会阻塞当前线程,另外一方面可能形成死锁deadlock,咱们验证一下

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queueConcurrent, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_barrier_sync(queueConcurrent, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"4--%@",[NSThread currentThread]);
    });
    NSLog(@"end");
复制代码

image.png

咱们看到dispatch_barrier_sync确实阻塞了当前线程,end是在dispatch_barrier_sync以后输出的

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    dispatch_barrier_sync(queueConcurrent, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
        dispatch_sync(queueConcurrent, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
    });
复制代码

image.png

不知道这是否是文档中所说的Calling this function and targeting the current queue results in deadlock.反正实现了deadlock效果

另外文档提到了Block_copy

image.png

我从源码看到确实dispatch_barrier_async调用了_dispatch_Block_copy

image.png

image.png

dispatch_barrier_sync是没有copy,这多是由于dispatch_barrier_sync阻塞线程后面代码不执行,而dispatch_barrier_async没有阻塞线程,那么后面就可能对函数体修改????因此拷贝一份这里留下问好

GCD 延时执行方法:dispatch_after

2秒以后将任务添加到主队列,具体何时执行还要看CUP的调度

dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), mainQueue, ^{
       //2秒以后将任务添加到主队列,具体何时执行还要看CUP的调度
        NSLog(@"执行了");
    });
复制代码

GCD 一次性代码(只执行一次):dispatch_once

实现单例

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       //单例
    });
复制代码

更多内容能够看一下# iOS多线程之dispatch_once剖析

GCD 快速迭代方法:dispatch_apply

先来看一下文档dispatch_apply

image.png

能够看到dispatch_apply和可重入且安全的并发队列能够实现高效的遍历操做,若是是一个串行队列那么就体现不出来他的高效之处了

dispatch_apply(10, queueGlobal, ^(size_t index) {
        NSLog(@"%zd--%@",index,[NSThread currentThread]);
    });
    NSLog(@"end");
复制代码

image.png

dispatch_apply会利用多个线程来遍历,不光是子线程,还能够调用主线程,另外他还会阻塞当前线程

Dispatch Group

image.png

dispatch_group做为一个单元监控一组任务。你能够将一组任务放到一个组里经过dispatch_group同步他们的行为。你能够将这些任务放在组里之后在同一个队列或者不一样的队列异步执行,能够在不阻塞当前线程的状况下监听这些异步任务执行完毕,也能够阻塞当前线程等待这些任务完成。

dispatch_group_notify

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        sleep(1);
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_notify(group, queueGlobal, ^{
        NSLog(@"notify--%@",[NSThread currentThread]);
    });
    NSLog(@"end--%@",[NSThread currentThread]);
复制代码

image.png

dispatch_group_notify并无阻塞当前线程,他是在当前组的其余队列都执行完成以后再执行,咱们能够将dispatch_group_notify任务放在主队列执行,这就实现了回调主线程的功能

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queueMain = dispatch_get_main_queue();
    dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueSerial, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueSerial, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_notify(group, queueMain, ^{
        NSLog(@"notify--%@",[NSThread currentThread]);
    });
    NSLog(@"end--%@",[NSThread currentThread]);
复制代码

image.png

dispatch_group_wait

阻塞当前线程,等待组内任务都执行完成才会继续执行

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
    NSLog(@"end--%@",[NSThread currentThread]);
复制代码

image.png

直到前面三个任务都执行完成才会打印end,固然也能够将阻塞的超时时间设置小一些,即便前面任务没有完成,可是时间到了也会继续执行

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(3);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2*NSEC_PER_SEC)));
    NSLog(@"end--%@",[NSThread currentThread]);
复制代码

image.png

dispatch_group_enter、dispatch_group_leave

dispatch_group_async内部也是经过dispatch_group_enterdispatch_group_leave来实现的

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_enter(group);
    dispatch_async(queueGlobal, ^{
        sleep(3);
        NSLog(@"1--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_async(queueGlobal, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_async(queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, queueGlobal, ^{
        NSLog(@"notify--%@",[NSThread currentThread]);
    });

    NSLog(@"end--%@",[NSThread currentThread]);
复制代码

实现了和dispatch_group_async同样的效果 image.png

信号量semaphore

在开发中常常须要线程同步,那么信号量是一个很好的选择,dispatch_semaphore_signal信号量+1,dispatch_semaphore_wait信号量-1,若是信号量小于0那么阻塞当前线程,能够设置阻塞的超时时间

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    dispatch_async(queueGlobal, ^{
        sleep(3);//耗时操做
        NSLog(@"1--%@",[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);//信号量+1
    });

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信号量-1
    dispatch_async(queueMain, ^{
        NSLog(@"主线程");
    });
    NSLog(@"end--%@",[NSThread currentThread]);
复制代码

image.png

多线程访问同一个数据有可能形成数据不安全,例如

__block int i = 5;
    while (i>0) {
        dispatch_async(queueGlobal, ^{
            i--;
            NSLog(@"%d--%@",i,[NSThread currentThread]);
        });
    }
复制代码

image.png

因为多线程同时修改i致使结果和预期出现了很大的出入,甚至NSLog函数的打印都出问题了😂,经过信号量能够解决这个问题

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block int i = 5;
    
    while (i>0) {
        dispatch_async(queueGlobal, ^{
            i--;
            NSLog(@"%d--%@",i,[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
复制代码

这个时候咱们看到结果按照预期输出了,由于信号量控制最多只有一个线程能够访问变量i(不必定是同一个线程,但同一时间最多只能有一个线程访问)

image.png

dispatch_set_target_queue

dispatch_set_target_queue有两个做用一是改变队列优先级,二是让多个串行队列之间也能串行地执行任务。

咱们前边所学的方法都是控制队列内部的任务的操做顺序,可是不一样队列之间是没有依赖关系的,假如咱们把A任务放在自定义串行队列serialQueueA中,把B任务放在自定义并发队列conQueueB中,那么咱们不知道任务A和任务B的前后执行顺序,有了dispatch_set_target_queue咱们能够利用其改变队列优先级的做用实现优先队列

改变队列优先级实现优先队列

dispatch_queue_create 建立的队列,不管是串行仍是并发,其优先级都是 DISPATCH_QUEUE_PRIORITY_DEFAULT,使用 dispatch_set_target_queue 能够改变队列优先级,注意改变的是优先调度开始顺序,而不是结束顺序,先被调度的不必定先结束执行

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("serialQueue3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("serialQueue4", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue5 = dispatch_queue_create("serialQueue5", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue6 = dispatch_queue_create("serialQueue6", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue7 = dispatch_queue_create("serialQueue7", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue8 = dispatch_queue_create("serialQueue8", DISPATCH_QUEUE_SERIAL);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
    });
    
    dispatch_async(serialQueue3, ^{
        NSLog(@"taskC");
    });
    
    dispatch_async(serialQueue4, ^{
        NSLog(@"taskD");
    });
    
    dispatch_async(serialQueue5, ^{
        NSLog(@"taskE");
    });
    
    dispatch_async(serialQueue6, ^{
        NSLog(@"taskF");
    });

    dispatch_async(serialQueue7, ^{
        NSLog(@"taskG");
    });

    dispatch_async(serialQueue8, ^{
        NSLog(@"taskH");
    });
复制代码

image.png 这个时候咱们不能肯定ABCDEFGH谁先执行,简单修改一下

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("serialQueue3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("serialQueue4", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue5 = dispatch_queue_create("serialQueue5", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue6 = dispatch_queue_create("serialQueue6", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue7 = dispatch_queue_create("serialQueue7", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue8 = dispatch_queue_create("serialQueue8", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    dispatch_set_target_queue(serialQueue2, globalQueueLow);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
    });
    
    dispatch_async(serialQueue3, ^{
        NSLog(@"taskC");
    });
    
    dispatch_async(serialQueue4, ^{
        NSLog(@"taskD");
    });
    
    dispatch_async(serialQueue5, ^{
        NSLog(@"taskE");
    });
    
    dispatch_async(serialQueue6, ^{
        NSLog(@"taskF");
    });

    dispatch_async(serialQueue7, ^{
        NSLog(@"taskG");
    });

    dispatch_async(serialQueue8, ^{
        NSLog(@"taskH");
    });
复制代码

咱们增长了两行代码

dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_set_target_queue(serialQueue2, globalQueueLow);
复制代码

image.png

网上看了好多例子都是到此为止,以致于我任务只是由于我给globalQueueLow设置的优先级为DISPATCH_QUEUE_PRIORITY_BACKGROUND因此serialQueue2的优先级才笔另外几个串行队列低,若是我设置优先级为DISPATCH_QUEUE_PRIORITY_HIGH那么队列就会优先调度

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("serialQueue3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("serialQueue4", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue5 = dispatch_queue_create("serialQueue5", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue6 = dispatch_queue_create("serialQueue6", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue7 = dispatch_queue_create("serialQueue7", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue8 = dispatch_queue_create("serialQueue8", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_queue_t globalQueueHight = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    
    dispatch_set_target_queue(serialQueue2, globalQueueLow);
    dispatch_set_target_queue(serialQueue3, globalQueueHight);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
    });
    
    dispatch_async(serialQueue3, ^{
        NSLog(@"taskC");
    });
    
    dispatch_async(serialQueue4, ^{
        NSLog(@"taskD");
    });
    
    dispatch_async(serialQueue5, ^{
        NSLog(@"taskE");
    });
    
    dispatch_async(serialQueue6, ^{
        NSLog(@"taskF");
    });

    dispatch_async(serialQueue7, ^{
        NSLog(@"taskG");
    });

    dispatch_async(serialQueue8, ^{
        NSLog(@"taskH");
    });
复制代码

image.png

多运行几回咱们发现设置DISPATCH_QUEUE_PRIORITY_HIGH优先级以后串行队列serialQueue3并无比其余优先级高,而仅仅比serialQueue2高了而已,咱们前边也说过,这种优先级高仅仅是开始调度的优先级高,而不是结束调度的优先级,若是咱们给任务A设置一个耗时任务就能够验证

dispatch_async(serialQueue1, ^{
        int a=0;
        for (int i=0; i<1000000; i++) {
            a++;
        }
        NSLog(@"%d-taskA",a);
    });
复制代码

image.png

这时A并无先结束执行,BC也没有最后结束执行

若是咱们设置

dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_set_target_queue(serialQueue3, mainQueue);
复制代码

这个时候是否是serialQueue3的优先级会不会很高呢,其实并无

image.png

看了官方文档也是很难理解dispatch_set_target_queue,我我的理解是若是经过dispatch_set_target_queue设置队列优先级的这些队列他们都成了二级公民,依赖于同一个队列的子队列之间能够经过优先级来设置,可是他们的优先级都要比没有设置过优先级的队列优先级要低了

image.png

咱们可使用dispatch_set_target_queue+dispatch_suspend/dispatch_resume实现一个优先队列

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    
    dispatch_set_target_queue(serialQueue1, serialQueue2);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_suspend(serialQueue1);
    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
        dispatch_resume(serialQueue1);
    });

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskC");
    });
复制代码

参考文章

iOS 多线程:『GCD』详尽总结

Dispatch

源码libdispatch

iOS多线程之dispatch_once剖析

dispatch_set_target_queue

《iOS 与 OS X 多线程和内存管理》读书笔记之 GCD(二)

相关文章
相关标签/搜索