iOS中GCD详解

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


简介

GCD

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

优势

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

核心概念

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

队列

使用dispatch_queue_create函数创建队列

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

队列有四个优先级

     #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是手动创建一个新的队列。

并发队列(Concurrent Dispatch Queue)

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

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);
复制代码

串行队列(Serial Dispatch Queue)

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

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、定制任务,确定想做的事情
  • 2、将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出

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”。


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

Supongo que te gusta

Origin juejin.im/post/7073297380061216776
Recomendado
Clasificación