美文网首页iOS多线程
iOS中GCD的应用

iOS中GCD的应用

作者: HuyaRC | 来源:发表于2019-03-15 09:36 被阅读0次

    分享是每个优秀的程序员所必备的品质


    简介

    GCD

    • 全称是Grand Central Dispatch :伟大的中枢调度器
    • 纯C语言,提供了非常多强大的函数

    优势

    • GCD是苹果公司为多核的并行运算提出的解决方案
    • GCD会自动利用更多的CPU内核(比如双核、四核)
    • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
    • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

    核心概念

    • 任务:执行什么操作
    • 队列:用来存放任务

    队列

    并发队列(Concurrent Dispatch Queue)

    • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    • 并发功能只有在异步(dispatch_async)函数下才有效

    串行队列(Serial Dispatch Queue)

    • 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

    GCD的使用步骤
    1、定制任务,确定想做的事情
    2、将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出

    并发队列

    使用dispatch_queue_create函数创建队列

         // 参数1:队列名称,注意传c字符 如:"RC"
         // 参数2:队列的类型  DISPATCH_QUEUE_CONCURRENT:并发 ,SERIAL:串行
        dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
    

    GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
    使用dispatch_get_global_queue函数获得全局的并发队列

        // 参数1: 队列的优先级 dispatch_queue_priority_t
        // 参数2: unsigned long flags ,此参数暂时无用,用0即可
        dispatch_queue_t queue = dispatch_get_global_queue(0, 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 // 后台
    

    补充:全局队列在整个应用程序中是默认存在的,并且对应有高、默认、低和后台优先级一共四个并发队列,使用的时候只是选择其中一个直接拿来使用。而使用create是手动创建一个新的队列。

    串行队列队列

    GCD中获得串行有2种途径
    1、使用dispatch_queue_create函数创建串行队列

    // 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t queue = dispatch_queue_create("RC", NULL); 
    

    2、使用主队列(跟主线程相关联的队列)

    • 主队列是GCD自带的一种特殊的串行队列
    • 放在主队列中的任务,都会放到主线程中执行
      使用dispatch_get_main_queue()获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue()
    

    GCD基本使用

    1. 异步函数+并发队列 :开启多条线程,并发执行任务
    - (void)asyncConcurrent{
        // 1.创建队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        NSLog(@"---satrt----");
        // 2.添加任务
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[3650:175994] ---satrt----
    RCGCDDemo[3650:175994] ---end----
    RCGCDDemo[3650:176035] 2----<NSThread: 0x600002895d40>{number = 3, name = (null)}
    RCGCDDemo[3650:176114] 1----<NSThread: 0x600002895c80>{number = 4, name = (null)}
    RCGCDDemo[3650:176115] 3----<NSThread: 0x60000289ae80>{number = 5, name = (null)}
    

    注意:并不是一个任务对应开启一条线程,具体需要开启多少条线程由系统决定

    2.异步函数+串行队列:开启一条线程,串行执行任务

    - (void)asyncSerial{
        //1.创建队列
        dispatch_queue_t queue = dispatch_queue_create("RC",  DISPATCH_QUEUE_SERIAL);
        NSLog(@"---satrt----");
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[3765:181439] ---satrt----
    RCGCDDemo[3765:181439] ---end----
    RCGCDDemo[3765:181473] 1----<NSThread: 0x600001013740>{number = 3, name = (null)}
    RCGCDDemo[3765:181473] 2----<NSThread: 0x600001013740>{number = 3, name = (null)}
    RCGCDDemo[3765:181473] 3----<NSThread: 0x600001013740>{number = 3, name = (null)}
    

    3 .同步函数+并发队列:不开线程,串行执行任务

    -(void)syncConcurrent{
        dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"---start---");
        dispatch_sync(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[3825:184734] ---start---
    RCGCDDemo[3825:184734] 1----<NSThread: 0x600000c19400>{number = 1, name = main}
    RCGCDDemo[3825:184734] 2----<NSThread: 0x600000c19400>{number = 1, name = main}
    RCGCDDemo[3825:184734] 3----<NSThread: 0x600000c19400>{number = 1, name = main}
    RCGCDDemo[3825:184734] ---end----
    

    4.同步函数+串行队列:不开线程,串行执行任务

    -(void)syncSerial{
        dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
        NSLog(@"---start---");
        dispatch_sync(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[3909:189316] ---start---
    RCGCDDemo[3909:189316] 1----<NSThread: 0x600003644fc0>{number = 1, name = main}
    RCGCDDemo[3909:189316] 2----<NSThread: 0x600003644fc0>{number = 1, name = main}
    RCGCDDemo[3909:189316] 3----<NSThread: 0x600003644fc0>{number = 1, name = main}
    RCGCDDemo[3909:189316] ---end----
    

    5.异步函数+主队列:不开线程,在主线程中串行执行任务

    //异步函数+主队列
    -(void)asyncMain{
        dispatch_queue_t queue = dispatch_get_main_queue();
        NSLog(@"---start---");
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[4050:195922] ---start---
    RCGCDDemo[4050:195922] ---end----
    RCGCDDemo[4050:195922] 1----<NSThread: 0x6000013ead80>{number = 1, name = main}
    RCGCDDemo[4050:195922] 2----<NSThread: 0x6000013ead80>{number = 1, name = main}
    RCGCDDemo[4050:195922] 3----<NSThread: 0x6000013ead80>{number = 1, name = main}
    

    6.同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
    注意:该方法在主线程中执行发生死锁;该方法在子线程中执行,那么所有的任务在主线程中执行。

    -(void)syncMain{
        dispatch_queue_t queue = dispatch_get_main_queue();
        NSLog(@"start----");
        dispatch_sync(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        NSLog(@"end---");
    }
    

    在主线程中执行-(void)syncMain方法,会因为相互等待而发生死锁,程序崩溃!
    如果在主线程执行该方法,该任务1、2、3因为加入了主队列所以也会在主线程中执行。当执行到任务1的时候,因为当前主线程正在等待任务1、2、3执行结束,才会再执行其他任务,这就造成了任务1等待-(void)syncMain方法执行结束,而-(void)syncMain需要方法里面的所以任务执行完毕才会结束(需要任务1、2、3执行完毕),-(void)syncMain和任务1相互等待造成死锁!!!

    在子线程中执行的打印:

    RCGCDDemo[4104:198438] start----
    RCGCDDemo[4104:198392] 1----<NSThread: 0x600001f2c700>{number = 1, name = main}
    RCGCDDemo[4104:198392] 2----<NSThread: 0x600001f2c700>{number = 1, name = main}
    RCGCDDemo[4104:198392] 3----<NSThread: 0x600001f2c700>{number = 1, name = main}
    RCGCDDemo[4104:198438] end---
    

    GCD线程间通信

    以在子线程下载,主线程显示为例

    - (void)laodImg{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // 下载图片操作
            dispatch_sync(dispatch_get_main_queue(), ^{
                // 显示图片
            });
        });
    }
    

    GCD常用函数

    延迟函数
    - (void)afterDelay{
        //1. 延迟执行的第一种方法
        //[self performSelector:@selector(task) withObject:nil afterDelay:1.0];
        
        //2.延迟执行的第二种方法
        //[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
       
        //3.GCD
        /* 参数1: DISPATCH_TIME_NOW 从现在开始计算时间
         * 参数2: 延迟的时间 1.0 GCD时间单位:纳秒
         * 参数3: 队列
         */
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), queue, ^{
            // 
        });
    }
    
    栅栏函数(控制任务的执行顺序)

    有两个函数:dispatch_barrier_asyncdispatch_barrier_sync
    注意:官方说明在使用栅栏函数时,使用自定义队列才有意义,如果用的是串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数

    dispatch_barrier_async

    代码:

    - (void)barrierAsync{
        dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"---satrt----");
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_barrier_async(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"4----%@",[NSThread currentThread]);
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[4559:227700] ---satrt----
    RCGCDDemo[4559:227700] ---end----
    RCGCDDemo[4559:230128] 2----<NSThread: 0x600002f5f0c0>{number = 8, name = (null)}
    RCGCDDemo[4559:228512] 1----<NSThread: 0x600002f5e840>{number = 5, name = (null)}
    RCGCDDemo[4559:228512] 3----<NSThread: 0x600002f5e840>{number = 5, name = (null)}
    RCGCDDemo[4559:228512] 4----<NSThread: 0x600002f5e840>{number = 5, name = (null)}
    

    可得:

    • barrier前面的任务都是并发执行的
    • 执行到barrier时,不用执行完barrier,就可以将后面的任务插入到队列中,但是仍然需要执行完barrier,才会执行后面的任务。

    dispatch_barrier_sync

    - (void)barrierSync{
        dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"---satrt----");
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_barrier_sync(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"4----%@",[NSThread currentThread]);
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[4653:234416] ---satrt----
    RCGCDDemo[4653:234775] 1----<NSThread: 0x600001783580>{number = 5, name = (null)}
    RCGCDDemo[4653:235167] 2----<NSThread: 0x60000178e6c0>{number = 8, name = (null)}
    RCGCDDemo[4653:234416] 3----<NSThread: 0x6000017d1400>{number = 1, name = main}
    RCGCDDemo[4653:234416] ---end----
    RCGCDDemo[4653:235167] 4----<NSThread: 0x60000178e6c0>{number = 8, name = (null)}
    

    可得:

    • 同样,barrier前面的任务都是并发执行的
    • 运行到barrier时,必须等到barrier执行结束后才会将后面的任务插入到队列中,继续执行。
    一次性代码

    注意:不能放到懒加载,主要用于单例类iOS单例类详解

    快速迭代

    开多个线程并发完成迭代操作

    - (void)applyTest{
        // 开子线程和主线程一起完成遍历任务,任务的执行时并发的
        /*
         参数1: 遍历的次数
         参数2: 队列(并发队列)
         参数3: index 索引
         */
        dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) {
            //添加耗时操作,效果更明显
            for(NSInteger i=0;i<100000;i++){
            }
            NSLog(@"%zd---%@",index,[NSThread currentThread]);
        });
    }
    

    打印:

    RCGCDDemo[4969:248588] 0---<NSThread: 0x600002fd5140>{number = 1, name = main}
    RCGCDDemo[4969:248654] 2---<NSThread: 0x600002f872c0>{number = 3, name = (null)}
    RCGCDDemo[4969:248652] 1---<NSThread: 0x600002f87080>{number = 4, name = (null)}
    RCGCDDemo[4969:248655] 3---<NSThread: 0x600002f87280>{number = 5, name = (null)}
    RCGCDDemo[4969:248588] 4---<NSThread: 0x600002fd5140>{number = 1, name = main}
    
    队列组

    作用与栅栏函数相似:当队列组中所有的任务都执行完毕的时候,会发出通知
    dispatch_group_t group = dispatch_group_create(); 创建创建队列组,
    dispatch_group_notify 拦截队列组中所有任务完成的通知
    代码:

    - (void)groupTest{
        // 1.创建队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        // 2.创建队列组
        dispatch_group_t group = dispatch_group_create();
        NSLog(@"---satrt----");
        // 3. 添加任务,并监听任务的执行情况,通知group
        dispatch_group_async(group, queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        //拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
        dispatch_group_notify(group, queue, ^{
            NSLog(@"-------come,baby");
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[934:25071] ---satrt----
    RCGCDDemo[934:25071] ---end----
    RCGCDDemo[934:25126] 1----<NSThread: 0x600003ec8640>{number = 5, name = (null)}
    RCGCDDemo[934:25125] 3----<NSThread: 0x600003ecc800>{number = 4, name = (null)}
    RCGCDDemo[934:25124] 2----<NSThread: 0x600003ec8740>{number = 6, name = (null)}
    RCGCDDemo[934:25124] -------come,baby
    

    可知:使用dispatch_group_notify是可以监听队列组中的异步函数执行情况,并且本身也是异步执行的,不会阻塞线程。

    还可以使用dispatch_group_enterdispatch_group_leave来达到相同的功能。dispatch_group_enter可以使得后面的异步任务会被纳入到队列组的监听范围,进入群组,两个方法必须要配对使用
    代码:

    - (void)groupTest2{
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_group_t group = dispatch_group_create();
        NSLog(@"---satrt----");
        
        //后面的异步任务会被纳入到队列组的监听范围
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
            //离开群组
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
            dispatch_group_leave(group);
        });
        
        //拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
        dispatch_group_notify(group, queue, ^{
            NSLog(@"-------come,baby");
        });
        NSLog(@"---end----");
    }
    

    打印:

    RCGCDDemo[993:28534] ---satrt----
    RCGCDDemo[993:28534] ---end----
    RCGCDDemo[993:28621] 1----<NSThread: 0x6000002af600>{number = 3, name = (null)}
    RCGCDDemo[993:28868] 3----<NSThread: 0x6000002afbc0>{number = 5, name = (null)}
    RCGCDDemo[993:28867] 2----<NSThread: 0x6000002a2340>{number = 4, name = (null)}
    RCGCDDemo[993:28867] -------come,baby
    

    还可以使用dispatch_group_wait来监听队列组中任务的执行情况,本身是阻塞的,直到队列组中所有的任务执行结束后才会执行后面方法。

    // 代码同上
    // 阻塞的 直到队列组中所有的任务都执行完毕之后才能执行
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        NSLog(@"---end----");
    

    打印:

    RCGCDDemo[1054:32183] ---satrt----
    RCGCDDemo[1054:32242] 1----<NSThread: 0x600000f5f480>{number = 3, name = (null)}
    RCGCDDemo[1054:32241] 3----<NSThread: 0x600000f5f780>{number = 6, name = (null)}
    RCGCDDemo[1054:32240] 2----<NSThread: 0x600000f5f300>{number = 4, name = (null)}
    RCGCDDemo[1054:32183] ---end----
    

    可知:等任务1、2、3全部执行完毕后,才会打印后面的 “end”。

    GCDDemo

    个人浅见,有误的地方欢迎指正

    相关文章

      网友评论

        本文标题:iOS中GCD的应用

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