GCD的前世今生

GCD

1、GCD全称 Grand Central Dispatch ,是纯C语言,提供了非常多强大的函数,来进行系统线程的管理。
2、优势:GCD是苹果公司为多核的并行运算提出的解决方案。GCD会自动利用更多的CPU内核,会自动管理线程的生命周期(创建线程、调度任务、销毁线程),程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码。

并发 串行 主队列
同步(sync) 没有开启新线程 串行执行任务 没有开启新线程 串行执行 锁死
异步(async) 有开启新线程 并行执行 有开启新线程 串行执行 没开启新线程 串行执行

串行 (DISPATCH_QUEUE_SERIAL)
并发 (DISPATCH_QUEUE_CONCURRENT)

四种组合方式

同步执行 + 并发队列

/**
 同步 + 并发
 在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
 */
- (void)synchronousAndConcurrent{

    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"synchronousAndConcurrent---begin"); //线程开始

    dispatch_queue_t queue = dispatch_queue_create("com.leocy.gcd", DISPATCH_QUEUE_CONCURRENT);

    dispatch_sync(queue, ^{
        // 添加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_sync(queue, ^{
        // 添加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_sync(queue, ^{
        // 添加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

     NSLog(@"synchronousAndConcurrent---end"); //线程结束
}

运行结果:没开启新线程,串行执行

2019-01-15 17:31:46.958190+0800 GCD使用[11901:3502931] currentThread—<NSThread: 0x283c32e00>{number = 1, name = main}
2019-01-15 17:31:46.958258+0800 GCD使用[11901:3502931] synchronousAndConcurrent—begin
2019-01-15 17:31:48.959827+0800 GCD使用[11901:3502931] 1—<NSThread: 0x283c32e00>{number = 1, name = main}
2019-01-15 17:31:50.961196+0800 GCD使用[11901:3502931] 1—<NSThread: 0x283c32e00>{number = 1, name = main}
2019-01-15 17:31:52.962809+0800 GCD使用[11901:3502931] 2—<NSThread: 0x283c32e00>{number = 1, name = main}
2019-01-15 17:31:54.963861+0800 GCD使用[11901:3502931] 2—<NSThread: 0x283c32e00>{number = 1, name = main}
2019-01-15 17:31:56.965229+0800 GCD使用[11901:3502931] 3—<NSThread: 0x283c32e00>{number = 1, name = main}
2019-01-15 17:31:58.966589+0800 GCD使用[11901:3502931] 3—<NSThread: 0x283c32e00>{number = 1, name = main}
2019-01-15 17:32:02.847758+0800 GCD使用[11901:3502931] synchronousAndConcurrent—end
(lldb)

异步执行 + 并发队列

/**
 异步 + 并发
 可以开启多个线程,任务交替(同时)执行。
 */
- (void)asynchronousAndConcurrent{

    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"synchronousAndSerial---begin"); //线程开始

    dispatch_queue_t queue = dispatch_queue_create("com.leocy.gcd", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        // 添加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_async(queue, ^{
        // 添加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_async(queue, ^{
        // 添加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    NSLog(@"asynchronousAndConcurrent---end"); //线程结束
}

执行结果:开启新线程,并发执行

2019-01-15 17:34:40.553187+0800 GCD使用[11912:3504396] currentThread—<NSThread: 0x281dbee40>{number = 1, name = main}
2019-01-15 17:34:40.553228+0800 GCD使用[11912:3504396] synchronousAndSerial—begin
2019-01-15 17:34:40.553262+0800 GCD使用[11912:3504396] asynchronousAndConcurrent—end
2019-01-15 17:34:44.382094+0800 GCD使用[11912:3504445] 3—<NSThread: 0x281df02c0>{number = 5, name = (null)}
2019-01-15 17:34:44.382096+0800 GCD使用[11912:3504446] 1—<NSThread: 0x281dd4540>{number = 4, name = (null)}
2019-01-15 17:34:44.382237+0800 GCD使用[11912:3504443] 2—<NSThread: 0x281de2100>{number = 3, name = (null)}
2019-01-15 17:34:46.385780+0800 GCD使用[11912:3504443] 2—<NSThread: 0x281de2100>{number = 3, name = (null)}
2019-01-15 17:34:46.385779+0800 GCD使用[11912:3504445] 3—<NSThread: 0x281df02c0>{number = 5, name = (null)}
2019-01-15 17:34:46.385779+0800 GCD使用[11912:3504446] 1—<NSThread: 0x281dd4540>{number = 4, name = (null)}

同步执行 + 串行队列

/**
 同步 + 串行
 不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。
 */
- (void)synchronousAndSerial{

    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"synchronousAndSerial---begin"); //线程开始

    dispatch_queue_t queue = dispatch_queue_create("com.leocy.gcd", DISPATCH_QUEUE_SERIAL);

    dispatch_sync(queue, ^{
        // 添加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_sync(queue, ^{
        // 添加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_sync(queue, ^{
        // 添加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    NSLog(@"synchronousAndSerial---end"); //线程结束
}

执行结果:没开启新线程,串行执行

2019-01-15 17:33:37.132737+0800 GCD使用[11907:3503870] currentThread—<NSThread: 0x2815e2e40>{number = 1, name = main}
2019-01-15 17:33:37.132774+0800 GCD使用[11907:3503870] synchronousAndSerial—begin
2019-01-15 17:33:39.134268+0800 GCD使用[11907:3503870] 1—<NSThread: 0x2815e2e40>{number = 1, name = main}
2019-01-15 17:33:41.135846+0800 GCD使用[11907:3503870] 1—<NSThread: 0x2815e2e40>{number = 1, name = main}
2019-01-15 17:33:43.137197+0800 GCD使用[11907:3503870] 2—<NSThread: 0x2815e2e40>{number = 1, name = main}
2019-01-15 17:33:45.138594+0800 GCD使用[11907:3503870] 2—<NSThread: 0x2815e2e40>{number = 1, name = main}
2019-01-15 17:33:47.140036+0800 GCD使用[11907:3503870] 3—<NSThread: 0x2815e2e40>{number = 1, name = main}
2019-01-15 17:33:49.141438+0800 GCD使用[11907:3503870] 3—<NSThread: 0x2815e2e40>{number = 1, name = main}
2019-01-15 17:33:49.141599+0800 GCD使用[11907:3503870] synchronousAndSerial—end
(lldb)

异步执行 + 串行队列

/**
 异步 + 串行
 特点:会开启一条新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。
 */
- (void)asynchronousAndSerial{
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"synchronousAndSerial---begin"); //线程开始

    dispatch_queue_t queue = dispatch_queue_create("com.leocy.gcd", DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{
        // 添加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_async(queue, ^{
        // 添加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_async(queue, ^{
        // 添加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

     NSLog(@"synchronousAndSerial---end"); //线程结束
}

执行结果:开启一条新线程,串行执行

2019-01-15 17:35:35.597125+0800 GCD使用[11917:3504860] currentThread—<NSThread: 0x281265b80>{number = 1, name = main}
2019-01-15 17:35:35.597163+0800 GCD使用[11917:3504860] synchronousAndSerial—begin
2019-01-15 17:35:35.597196+0800 GCD使用[11917:3504860] synchronousAndSerial—end
2019-01-15 17:35:38.944090+0800 GCD使用[11917:3504894] 1—<NSThread: 0x281207740>{number = 4, name = (null)}
2019-01-15 17:35:40.949580+0800 GCD使用[11917:3504894] 1—<NSThread: 0x281207740>{number = 4, name = (null)}
2019-01-15 17:35:42.955068+0800 GCD使用[11917:3504894] 2—<NSThread: 0x281207740>{number = 4, name = (null)}
2019-01-15 17:35:44.960644+0800 GCD使用[11917:3504894] 2—<NSThread: 0x281207740>{number = 4, name = (null)}
2019-01-15 17:35:46.966089+0800 GCD使用[11917:3504894] 3—<NSThread: 0x281207740>{number = 4, name = (null)}
2019-01-15 17:35:48.971523+0800 GCD使用[11917:3504894] 3—<NSThread: 0x281207740>{number = 4, name = (null)}

特殊情况

同步执行 + 主队列

/**
 * 同步 + 主队列
 * 特点(主线程调用):互等卡主不执行。
 * 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。
 */
- (void) synchronousAndMain {

    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"synchronousAndMain---begin");

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
//    dispatch_queue_t mainQueue = dispatch_queue_create("com.leocy.gcd", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(mainQueue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(mainQueue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(mainQueue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    NSLog(@"synchronousAndMain---end");
}

执行结果:主线程调用:死锁卡住不执行 其他线程调用:没有开启新线程,串行执行任务

异步执行 + 主队列

/**
 异步 +  主队列
 */
- (void)asynchrnousAndMain {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asynchrnousAndMain---begin");

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_async(mainQueue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_async(mainQueue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_async(mainQueue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    NSLog(@"asynchrnousAndMain---end");
}

执行结果:没开启新线程,串行执行

2019-01-15 17:39:53.716154+0800 GCD使用[11927:3506203] currentThread—<NSThread: 0x283c02e40>{number = 1, name = main}
2019-01-15 17:39:53.716196+0800 GCD使用[11927:3506203] asynchrnousAndMain—begin
2019-01-15 17:39:53.716226+0800 GCD使用[11927:3506203] asynchrnousAndMain—end
2019-01-15 17:39:57.868912+0800 GCD使用[11927:3506203] 1—<NSThread: 0x283c02e40>{number = 1, name = main}
2019-01-15 17:39:59.870304+0800 GCD使用[11927:3506203] 1—<NSThread: 0x283c02e40>{number = 1, name = main}
2019-01-15 17:40:01.871099+0800 GCD使用[11927:3506203] 2—<NSThread: 0x283c02e40>{number = 1, name = main}
2019-01-15 17:40:03.872303+0800 GCD使用[11927:3506203] 2—<NSThread: 0x283c02e40>{number = 1, name = main}
2019-01-15 17:40:05.873882+0800 GCD使用[11927:3506203] 3—<NSThread: 0x283c02e40>{number = 1, name = main}
2019-01-15 17:40:07.875393+0800 GCD使用[11927:3506203] 3—<NSThread: 0x283c02e40>{number = 1, name = main}

其他常用

栅栏 dispatch_barrier_async

dispatch_barrier_sync(queue, ^{
        NSLog(@"========栅栏=========");
});

效果:栅栏上的上方的先执行,下方的待上方执行完毕后执行

延时 dispatch_after

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 5.0秒后异步追加任务代码到主队列,并开始执行
        NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程
});

一次 dispatch_once (单例)

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
        <#code to be executed once#>
});

对列组 dispatch_group

/**
 * 队列组 dispatch_group_notify
 */
- (void)groupNotify {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");

    dispatch_group_t group =  dispatch_group_create();

    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
        NSLog(@"group---end");
    });
}

执行结果:等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务

2019-01-15 18:09:51.686244+0800 GCD使用[11955:3516270] currentThread—<NSThread: 0x2831871c0>{number = 1, name = main}
2019-01-15 18:09:51.686295+0800 GCD使用[11955:3516270] group—begin
2019-01-15 18:09:55.980170+0800 GCD使用[11955:3516409] 2—<NSThread: 0x283186380>{number = 5, name = (null)}
2019-01-15 18:09:55.980168+0800 GCD使用[11955:3516410] 1—<NSThread: 0x2831a8380>{number = 4, name = (null)}
2019-01-15 18:09:57.985010+0800 GCD使用[11955:3516410] 1—<NSThread: 0x2831a8380>{number = 4, name = (null)}
2019-01-15 18:09:57.985670+0800 GCD使用[11955:3516409] 2—<NSThread: 0x283186380>{number = 5, name = (null)}
2019-01-15 18:09:59.987117+0800 GCD使用[11955:3516270] 3—<NSThread: 0x2831871c0>{number = 1, name = main}
2019-01-15 18:10:01.988533+0800 GCD使用[11955:3516270] 3—<NSThread: 0x2831871c0>{number = 1, name = main}
2019-01-15 18:10:01.988660+0800 GCD使用[11955:3516270] group—end

监听 dispatch_group_notify

阻塞线程 dispatch_group_wait


上一篇
iOS富文本-让你的label绚烂起来 iOS富文本-让你的label绚烂起来
NSFontAttributeName 设置字体属性,默认值:字体:Helvetica(Neue) 字号:12NSForegroundColorAttributeNam 设置字体颜色,取值为 UICo
2018-11-13
下一篇
Weakself 与 Strongself的兄弟情 Weakself 与 Strongself的兄弟情
Weakself 与 Strongself1、什么时候用weakself? 主要看block是否被self持有,并且有没有产生循环引用 ,倘若都没有就不需要用weakself 2、什么时候用strongself? 主要是防止block执行过
2018-10-16
目录