美文网首页
2.多线程之GCD

2.多线程之GCD

作者: 王洛书 | 来源:发表于2016-08-06 14:49 被阅读0次

    1.GCD - Grand Central Dispatch 纯C语言;苹果为多核处理器的并行运算提供的解决方案,自动调度cpu内核,自动管理线程生命周期(创建线程、调度任务、销毁线程)。

    2.任务、队列

    任务:要执行的操作(逻辑代码)

    队列:存放任务。GCD会自动从队列中取出任务,在相应的线程中执行,遵循FIFO原则,先进先出,后进后出

    3.执行任务:同步、异步

    同步:不能开启新线程

    异步:可以开启新线程,可以创建多条线程,并发执行任务

    4.队列类型:并发、串行

    并发(Concurrent Dispatch Queue):多个任务同时执行,同时开启多个线程,每个线程执行一个任务,但是只能在异步下有效

    串行(Serial Dispatch Queue):多个任务排队按顺序执行

    5.任务与队列的四种组合方式:同步并发、同步串行、异步并发、异步串行。

    5.1 同步并发代码:

    // 1.获得全局的并发队列,供整个应用使用,不用手动创建

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2.将任务加入队列

    dispatch_sync(queue, ^{

    NSLog(@"1-----%@", [NSThread currentThread]);

    });

    dispatch_sync(queue, ^{

    NSLog(@"2-----%@", [NSThread currentThread]);

    });

    控制台输出:

    2016-08-06 11:48:17.716 07-GCD的基本使用[63024:4677093] 1-----{number = 1, name = main}

    2016-08-06 11:48:17.717 07-GCD的基本使用[63024:4677093] 2-----{number = 1, name = main}

    --- 对全局并发队列参数做一解释:

    第一个参数--队列优先级:默认使用第二个

    #define DISPATCH_QUEUE_PRIORITY_HIGH 2

    #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0

    #define DISPATCH_QUEUE_PRIORITY_LOW (-2)

    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

    第二个参数--unsigned long flags,此参数作为以后的扩展参数使用,目前传0就可以

    5.2 同步串行:不开启新线程,在当前线程执行任务,排队执行,一个接一个

    // 1.创建串行队列

    dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_SERIAL);

    // 2.将任务加入队列

    dispatch_sync(queue, ^{

    NSLog(@"1-----%@", [NSThread currentThread]);

    });

    dispatch_sync(queue, ^{

    NSLog(@"2-----%@", [NSThread currentThread]);

    });

    控制台输出:

    2016-08-06 14:22:40.753 07-GCD的基本使用[63168:4815767] 1-----{number = 1, name = main}

    2016-08-06 14:22:40.753 07-GCD的基本使用[63168:4815767] 2-----{number = 1, name = main}

    ---对手动创建线程参数做出解释:

    dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

    const char *label -- 线程标识,随便写一个唯一的 - 例如:"etong.queue"

    dispatch_queue_attr_t attr -- 队列属性

    #define DISPATCH_QUEUE_SERIAL NULL

    #define DISPATCH_QUEUE_CONCURRENT

    所以创建队列也可以这么写:dispatch_queue_t queue = dispatch_queue_create("etong.queue", NULL);

    5.3 异步并发:可同时开始多条线程,可以手动创建、也可以使用全局并发队列

    // 1.创建一个并发队列

    // label : 相当于队列的名字

    //    dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_CONCURRENT);

    // 1.获得全局的并发队列

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2.将任务加入队列

    dispatch_async(queue, ^{

    for (NSInteger i = 0; i<3; i++) {

    NSLog(@"1-----%@", [NSThread currentThread]);

    }

    });

    dispatch_async(queue, ^{

    for (NSInteger i = 0; i<3; i++) {

    NSLog(@"2-----%@", [NSThread currentThread]);

    }

    });

    控制台输出:

    2016-08-06 14:35:16.894 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}

    2016-08-06 14:35:16.894 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}

    2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}

    2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}

    2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}

    2016-08-06 14:35:16.896 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}

    要说明的是:异步并发函数块执行代码的顺序--是将函数块的代码执行完之后,才会返回去执行队列中任务。--重要

    5.4 异步串行 会开启新的线程,任务还是排队依次执行

    // 1.创建串行队列

    dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_SERIAL);

    //    dispatch_queue_t queue = dispatch_queue_create("etong.queue", NULL);

    // 2.将任务加入队列

    dispatch_async(queue, ^{

    NSLog(@"1-----%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

    NSLog(@"2-----%@", [NSThread currentThread]);

    });

    控制台输出:

    2016-08-06 14:40:40.694 07-GCD的基本使用[63202:4904803] 1-----{number = 2, name = (null)}

    2016-08-06 14:40:40.694 07-GCD的基本使用[63202:4904803] 2-----{number = 2, name = (null)}

    *还有2种主队列的情况

    5.5 异步+主队列:只在主线程中执行任务

    // 1.获得主队列

    dispatch_queue_t queue = dispatch_get_main_queue();

    // 2.将任务加入队列

    dispatch_async(queue, ^{

    NSLog(@"1-----%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

    NSLog(@"2-----%@", [NSThread currentThread]);

    });

    控制台输出:

    2016-08-06 14:43:13.921 07-GCD的基本使用[63217:4920120] 1-----{number = 1, name = main}

    2016-08-06 14:43:13.921 07-GCD的基本使用[63217:4920120] 2-----{number = 1, name = main}

    5.6 同步+主队列:这个比较奇葩

    NSLog(@"++++++++++++");

    // 1.获得主队列

    dispatch_queue_t queue = dispatch_get_main_queue();

    // 2.将任务加入队列

    dispatch_sync(queue, ^{

    NSLog(@"1-----%@", [NSThread currentThread]);

    });

    dispatch_sync(queue, ^{

    NSLog(@"2-----%@", [NSThread currentThread]);

    });

    dispatch_sync(queue, ^{

    NSLog(@"3-----%@", [NSThread currentThread]);

    });

    NSLog(@"-------------");

    控制台输出:

    2016-08-06 14:44:50.115 07-GCD的基本使用[63229:4929440] ++++++++++++

    -- 对同步+主队列输出解释一下:因为是同步,所以等待任务依次执行,但对于开启的多个任务来说,需要等待函数块执行完,执行队列中的任务,这样双方都在等待,导致程序卡死不动。

    6.线程间通信:基本上是子线程下载的数据需要在主线程完成加载刷新

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 图片的网络路径

    NSURL *url = [NSURL URLWithString:@"https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1537639143,1137876288&fm=58"];

    // 加载图片

    NSData *data = [NSData dataWithContentsOfURL:url];

    // 生成图片

    UIImage *image = [UIImage imageWithData:data];

    // 回到主线程

    dispatch_async(dispatch_get_main_queue(), ^{

    self.imageView.image = image;

    });

    });

    7.其他GCD函数的使用

    7.1 延时执行:有三种延时执行的方法

    7.1.1 调用NSObject方法

       [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

    7.1.2 使用GCD函数

       dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            NSLog(@"run-----");

        });

    解释一下:2.0是需要延时的秒数;dispatch_get_main_queue()根据需要创建队列

    7.1.3 调用NSTimer方法

    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];

    7.2 一次性GCD函数:保证block中代码在程序运行期间只被执行一次

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

    NSLog(@"------run");//只执行一次,默认是线程安全的

    });

    7.3 拦截函数

    dispatch_queue_t queue = dispatch_queue_create("123456", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{

    NSLog(@"----1-----%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

    NSLog(@"----2-----%@", [NSThread currentThread]);

    });

    dispatch_barrier_async(queue, ^{

    NSLog(@"----barrier-----%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

    NSLog(@"----3-----%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

    NSLog(@"----4-----%@", [NSThread currentThread]);

    });

    控制台输出:

    2016-08-06 15:06:40.230 09-掌握-GCD的其他常用函数[63329:5037819] ----1-----{number = 2, name = (null)}

    2016-08-06 15:06:40.230 09-掌握-GCD的其他常用函数[63329:5037834] ----2-----{number = 3, name = (null)}

    2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037834] ----barrier-----{number = 3, name = (null)}

    2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037834] ----3-----{number = 3, name = (null)}

    2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037819] ----4-----{number = 2, name = (null)}

    --解释一下:barrier-栅栏,起拦截作用,他前面的任务执行完毕才能继续往下执行。

    此方法创建的队列只能是手动创建,不能使用全局并发队列,否则不起作用

    7.4 队列组:需求--分别异步执行耗时操作,完毕后,回到主线程操作

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 创建一个队列组

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group, queue, ^{

    //执行一个耗时操作

    });

    dispatch_group_async(group, queue, ^{

    //执行一个耗时操作

    });

    dispatch_group_notify(group, queue, ^{

    dispatch_async(dispatch_get_main_queue(), ^{

    //回到主线程

    });

    });

    7.5 快速迭代函数:此方法可以在多条线程下同时完成任务

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_apply(subpaths.count, queue, ^(size_t index) {

    });

    -- 对快速迭代函数做出解释:for循环遍历10个变量的话,是顺序挨个遍历;而GCD快速迭代是同时开始10个线程一次遍历完,几乎是同时的。



    相关文章

      网友评论

          本文标题:2.多线程之GCD

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