iOS开发的中的多线程 --- GCD

多线程实现的几种方案

技术方案 简介 语言 线程生命周期 使用频率
pthread 一套通用的多线程方案
适用于Linux、Unix、macOS等平台
跨平台/可移植,使用难度大
C 程序员管理 机会不用
NSThread 苹果封装,更加面向对象
简单使用,可直接操作线程对象
OC 程序员管理 偶尔使用
GCD 旨在替代NSThread多线程技术
充分利用设备的多核
C 自动管理 经常使用
NSOperation 基于GCD(底层GCD)
但比GCD多了一些更简单实用的功能
使用更加面向对象
OC 自动管理 经常使用

GCD的简介

GCD中有两个很重要的概念: 任务队列队列中存放的就是多个任务队列的执行,就是执行其存放的各个任务

任务:即操作,说白了就是一段代码块,在GCD中就是一个block,所以添加任务十分方便。任务有同步sync)和异步async)的区分。主要的区别在于会不会阻塞当前的线程。

  • 同步任务(sync,即同步操作),它会阻塞当前线程,直到block中的任务执行完毕,然后当前线程才会继续执行。
  • 异步任务(async,即异步操作),它不会阻塞当前线程,当前线程会直接往下执行。

队列:用于存放任务,决定任务是顺序执行的还是并发执行的。队列对应的有两种:串行队列并行队列

  • 串行队列,根据定义,由于是队列(queue),它遵从队列的FIFO特性,即按照任务添加的顺序存依次执行。
  • 并行队列,放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。

线程(thread)与队列(queue)的关系:线程队列是两个不同的概念。队列是用来存放任务的,而线程是由系统去创建和管理,而且一个线程中可能有多个串行并行队列。

创建队列

  • 主队列:这是一个特殊的串行队列。主队列用于刷新UI,任何需要刷新UI的工作都是在主队列中执行,所以一般耗时的任务都是放到别的线程中执行。
//获取主队列,即主线程
dispatch_queue_t queue = dispatch_get_main_queue();
  • 手动创建队列:
dispatch_queue_create(const char *_Nullable label,
        dispatch_queue_attr_t _Nullable attr);

其中第一个参数是标识符,用于DEBUG时便于知道是哪个唯一的队列,可以为空。
第二个参数用来表示创建的队列是串行队列、还是并行队列
- DISPATCH_QUEUE_SERIAL或者NULL表示创建串行队列;
- DISPATCH_QUEUE_CONCURRENT表示创建的是并行队列。

  • 全局并发队列:系统提供的一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

创建任务

  • 同步任务:会阻塞当前线程(sync
  dispatch_sync(<#queue#>, ^{
      //code here
      NSLog(@"同步任务");
  });
  • 异步任务:不会阻塞(async
  dispatch_async(<#queue#>, ^{
      //code here
      NSLog(@"异步任务");
  });

组合代码测试

  • 同步任务 + 主队列
    代码:
//主队列的任务(A)
- (void)syncAndMainQueue {
    //同步任务 + 主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin --- %@", [NSThread currentThread]);
    for (NSInteger index = 0 ; index < 10; index ++) {
        //同步任务(B)
        dispatch_sync(mainQueue, ^{
            NSLog(@"index : %ld --- %@", index , [NSThread currentThread]);
        });
    }
    NSLog(@"end --- %@", [NSThread currentThread]);
}

打印信息:

同步任务+主队列.png

结论:程序卡死,由于是同步任务(B),会阻塞当前线程,等待加入到线程的同步任务执行完成之后才会继续当前任务,而由于主队列是串行队列,会按按顺序( FIFO)执行队列中添加的任务,所以同步任何会等待正在执行的任务执行完毕,这样就造成了相互等待,继而造成死锁。

  • 异步任务 + 主队列
    代码:
- (void)asyncAndMainQueue {
    //异步任务 + 主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin --- %@", [NSThread currentThread]);
    for (NSInteger index = 0 ; index < 10; index ++) {
        dispatch_async(mainQueue, ^{
            NSLog(@"index : %ld --- %@", index , [NSThread currentThread]);
        });
    }
    NSLog(@"end --- %@", [NSThread currentThread]);
}

打印信息:


iOS开发的中的多线程 --- GCD_第1张图片
异步任务 + 主队列.png

结论:在主线程串行执行。由于是异步任务,不会阻塞当前线程,由于主队列是串行队列,等待当前任务执行完成之后按顺序执行任务。

  • 同步任务 + 串行队列
    代码:
- (void)syncAndSerialQueue {
    //同步任务 + 串行队列
    dispatch_queue_t serailQueue = dispatch_queue_create("serialQueue", NULL);
    NSLog(@"begin --- %@", [NSThread currentThread]);
    for (NSInteger index = 0 ; index < 10; index ++) {
        dispatch_sync(serailQueue, ^{
            NSLog(@"index: %ld --- %@", index , [NSThread currentThread]);
        });
    }
    NSLog(@"end --- %@", [NSThread currentThread]);
}

打印:


iOS开发的中的多线程 --- GCD_第2张图片
同步任务 + 串行队列.png

结论:没有开启新线程,在当前线程执行该同步任务。由于是同步任务,会阻塞当前线程,而队列是串行队列,会按顺序执行任务,等到该队列中的所有任务执行完毕之后,当前线程才会继续执行下去。

  • 异步任务 + 串行队列
    代码:
- (void)asyncAndSerialQueue {
    //异步任务 + 串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
    NSLog(@"begin --- %@", [NSThread currentThread]);
    for (NSInteger index = 0; index < 10; index ++) {
        dispatch_async(serialQueue, ^{
            NSLog(@"index: %ld --- %@", index , [NSThread currentThread]);
        });
    }
    NSLog(@"end --- %@", [NSThread currentThread]);
}

打印:


iOS开发的中的多线程 --- GCD_第3张图片
异步任务 + 串行队列.png

结论:由于是异步任务,不会阻塞当前线程,开了新线程,由于是串行队列,会按顺序执行任务,所以只需开启一条新的线程。

  • 同步任务 + 并行队列
    代码:
- (void)syncAndConcurrent {
    //同步任务 + 并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin --- %@", [NSThread currentThread]);
    for (NSInteger index = 0; index < 10; index ++) {
        dispatch_sync(concurrentQueue, ^{
            NSLog(@"index: %ld --- %@", index , [NSThread currentThread]);
        });
    }
    NSLog(@"end --- %@", [NSThread currentThread]);    
}

打印:


iOS开发的中的多线程 --- GCD_第4张图片
同步任务 + 并行队列.png

结论:由于是同步任务,会阻塞当前线程,且不会开启新线程,所以同步任务会在当前线程执行完毕,等到完毕之后,当前线程执行执行。

  • 异步任务 + 并行队列
- (void)asyncAndConcurrent {
    //异步任务 + 并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin --- %@", [NSThread currentThread]);
    for (NSInteger index = 0; index < 10; index ++) {
        dispatch_async(concurrentQueue, ^{
            NSLog(@"index: %ld --- %@", index, [NSThread currentThread]);
        });
    }
    NSLog(@"end --- %@", [NSThread currentThread]);
}

打印


iOS开发的中的多线程 --- GCD_第5张图片
异步任务 + 并行队列.png

结论:由于是异步任务,不会阻塞当前线程,同时会开启新的线程,而且由于队列是并行队列,会开启多条线程,并发的执行这些异步任务。

总结

同步(sync) 异步(async)
串行队列 当前线程,顺序执行 其他线程,顺序执行
并行队列 当前线程,顺序执行 开多个线程,并发执行
主队列 程序死锁 主队列,顺序执行
    1. 开不开线程,取决于执行任务的函数,同步不开,异步开。
    1. 开几条线程,取决于队列,串行开一条,并发开多条(异步)
    1. 主队列: 专门用来在主线程上调度任务的"队列",主队列不能在其他线程中调度任务!

你可能感兴趣的:(iOS开发的中的多线程 --- GCD)