iOS 多线程 GCD

作者: 六月的第三天 | 来源:发表于2017-08-15 18:27 被阅读94次

    线程vs进程

    进程: 对于移动端来讲一个APP的启动就相当于手机系统开启了一个进程,每个程序只能开启一个进程,也就是说对于手机系统而言都是单进程应用。

    线程: 可以理解为独立执行的代码段,一个线程同时间只能执行一个任务。

    提示: iOS程序启动之后就创建了一个主线程,用于刷新程序UI,所以才有你看到的绚丽多彩的界面。

    同步和异步

    线程分同步sync和异步async之分

    • 同步线程:同步线程会阻塞当前线程去执行线程内的任务,执行完之后才会反回当前线程。
    • 异步线程:异步线程不会阻塞当前线程,会开启其他线程去执行线程内的任务。(推荐使用)
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步执行任务,不会阻塞当前线程");
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
      NSLog(@"同步执行任务,注意——>");//这段代码不会打印,会产生死锁,因为同步主线程会造成主队列线程相互等待。
    });
    
    

    队列

    GCD中一个block代码块就是一个队列,队列可以是并行也可以是串行的。默认情况下,它们是串行的,也就是说,任何给定的时间内,只能有一个单独的 block 运行。队列也可以是并行的,也就是同一时间内允许多个 block 一起执行。其实队列很大程度的帮助我们更方便的使用多线程来调度我们的功能。

    主线程队列

    1.获取主线程串行队列

    dispatch_queue_t mainQueue = dispatch_get_main_queue();//注意:禁止再次队列中同步执行任务。因为会产生死锁
    

    2.主线程串行队列异步执行任务,在主线程运行,不会产生死锁

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
        NSLog(@"主队列");
    });
    
    

    全局并发队列

    耗时的操作,比如读取网络数据,IO,数据库读写等,我们会在另外一个线程中处理这些操作,然后通知主线程更新界面
    1.获取全局并发队列

    //全局并发队列中 GCD 为我们提供了优先级
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    /*
     #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
     */
    
    

    2.多个线程任务在全局并发队列中执行

    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"current task");
    dispatch_async(globalQueue, ^{
        NSLog(@"最先加入全局并发队列");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"次加入全局并发队列");
    });
    NSLog(@"next task");
    

    控制台输出:

    2015-11-18 16:54:52.202 Whisper[39827:403208] current task
    2015-11-18 16:54:52.203 Whisper[39827:403208] next task
    2015-11-18 16:54:52.205 Whisper[39827:403309] 最先加入全局并发队列
    2015-11-18 16:54:52.205 Whisper[39827:403291] 次加入全局并发队列
    

    异步线程的执行顺序是不确定的。几乎同步开始执行
    全局并发队列由系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。

    自定义队列

    自定义队列就是抛开系统为我们创建的全局主队列以及全局并发队列,自己创建一个队列

    //自定义串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("com.custom.serialQueue", DISPATCH_QUEUE_SERIAL);
    //自定义并行队列
    dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.custom.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    

    dispatch_queue_create 后面第一个参数通常用反域名字符表示,你可以作为你识别的唯一名称,方便调试
    有了队列我们就可以添加任务来执行了

    示例:

    dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"current task");
    dispatch_async(conCurrentQueue, ^{
       NSLog(@"先加入队列");
    });
    dispatch_async(conCurrentQueue, ^{
       NSLog(@"次加入队列");
    });
    NSLog(@"next task");
    

    控制台输出如下:

    2015-11-19 10:45:22.290 Whisper[1050:26445] current task
    2015-11-19 10:45:22.290 Whisper[1050:26445] next task
    2015-11-19 10:45:22.290 Whisper[1050:26505] 次加入队列
    2015-11-19 10:45:22.290 Whisper[1050:26500] 先加入队列
    

    队列组

    当遇到需要执行多个线程并发执行,然后等多个线程都结束之后,再汇总执行结果时可以用group queue

    dispatch_group_t groupQueue = dispatch_group_create();
    

    使用示例:

    dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_group_t groupQueue = dispatch_group_create();
    NSLog(@"current task");
    dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
       NSLog(@"并行任务1");
    });
    dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
       NSLog(@"并行任务2");
    });
    dispatch_group_notify(groupQueue, mainQueue, ^{
       NSLog(@"groupQueue中的任务 都执行完成,回到主线程更新UI");
    });
    NSLog(@"next task");
    

    控制台输出:

    2017-08-15 17:42:42.355 TestProject[21907:682737] current task
    2017-08-15 17:42:42.356 TestProject[21907:682737] next task
    2017-08-15 17:42:42.356 TestProject[21907:682850] 并行任务1
    2017-08-15 17:42:42.356 TestProject[21907:682849] 并行任务2
    2017-08-15 17:42:42.504 TestProject[21907:682737] groupQueue中的任务 都执行完成,回到主线程更新UI
    
    //可以阻塞当前线程 起到延迟等待效果
    dispatch_group_wait(groupQueue, delayTime);
    

    GCD 系统常用的 dispatch 方法

    1.dispatch_once 实现单利模式

    + (UIColor *)defaultInstance;
    {
        static UIColor *color;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            color = [UIColor colorWithRed:0.380f green:0.376f blue:0.376f alpha:1.000f];
        });
        return color;
    }
    

    上面的 block 只会运行一次。并且在连续的调用中,这种检查是很高效的

    2.dispatch_after被用作延迟添加

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"current task");
    dispatch_after(delayTime3, mainQueue, ^{
      NSLog(@"3秒之后添加到队列");
    });
    dispatch_after(delayTime2, mainQueue, ^{
       NSLog(@"2秒之后添加到队列");
    });
    NSLog(@"next task");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延迟两秒执行");
    });
    

    3.dispatch_apply

    dispatch_apply函数的功能:把一项任务提交到队列中多次执行,队列可以是串行也可以是并行,dispatch_apply不会立刻返回,在执行完block中的任务后才会返回,是同步执行的函数。

    dispatch_queue_t seriaQueue = dispatch_queue_create("com.seriaQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_apply(3, seriaQueue, ^(size_t index) {
        index++;
        NSLog(@"index=%zu",index);
    });
    NSLog(@"执行完成");
    

    控制台输出:

    //如果 seriaQueue 为并行队列 index 也可能是无须输出
    2017-08-15 17:56:09.125 TestProject[22222:694749] index=1
    2017-08-15 17:56:09.125 TestProject[22222:694749] index=2
    2017-08-15 17:56:09.126 TestProject[22222:694749] index=3
    2017-08-15 17:56:09.126 TestProject[22222:694749] 执行完成
    

    4.dispatch_barrier_async 栅栏的作用

    功能:是在并行队列中,等待在dispatch_barrier_async之前加入的队列全部执行完成之后(这些任务是并发执行的)再执行dispatch_barrier_async中的任务,dispatch_barrier_async中的任务执行完成之后,再去执行在dispatch_barrier_async之后加入到队列中的任务(这些任务是并发执行的)。

    示例:

    dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(conCurrentQueue, ^{
      NSLog(@"dispatch 1");
    });
    dispatch_async(conCurrentQueue, ^{
       NSLog(@"dispatch 2");
    });
    dispatch_barrier_async(conCurrentQueue, ^{
       NSLog(@"dispatch barrier");
    });
    dispatch_async(conCurrentQueue, ^{
       NSLog(@"dispatch 3");
    });
    dispatch_async(conCurrentQueue, ^{
       NSLog(@"dispatch 4");
    });
    

    控制台输出:

    2015-11-19 18:12:34.125 Whisper[22633:297257] dispatch 1
    2015-11-19 18:12:34.125 Whisper[22633:297258] dispatch 2
    2015-11-19 18:12:34.126 Whisper[22633:297258] dispatch barrier
    2015-11-19 18:12:34.127 Whisper[22633:297258] dispatch 3
    2015-11-19 18:12:34.127 Whisper[22633:297257] dispatch 4
    

    相关文章

      网友评论

        本文标题:iOS 多线程 GCD

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