iOS多线程之GCD

我们平时在项目开发过程中经常会用到多线程相关的技术,经常会使用多线程来进行网络请求和数据传输等操作,OC中创建多线程主要有以下几种方式:

  • pthread
  • NSThread
  • GCD
  • NSOperation

这四种创建多线程区别对比如图:

iOS多线程之GCD_第1张图片
image

我们在平时的项目开发过程中,使用最多的创建多线程的方式就是GCD,接下来我们看下GCD的常见用法

GCD有两种执行任务的方式:

  • dispatch_snyc(同步执行任务):任务在当前主线程中执行,并没有开启新线程(dispatch_snyc不具备开启异步线程的能力)
  • dispatch_async(异步执行任务):任务在子线程中执行,dispatch_async会开启一个异步子线程(dispatch_async具备开启新线程的能力)

GCD队列(queue)也分为两种类型:

  • 串行队列(Serial Dispatch Queue):多个任务一个挨着一个的有序执行,上一个任务执行完接着执行下一个任务
  • 并发队列(Concurrent Dispatch Queue):多个任务并发(同时)执行,自动开启多个线程来同时执行任务

上面我们提到了两个概念

  • 任务:可以理解为是多线程需要做的事情,在GCD中任务就是对应的block代码块内需要执行的代码,任务又分为同步异步同步异步的主要区别在于是否具备开启新线程的能力

    • 同步:任务在当前主线程中执行,不具备开启新线程的能力
    • 异步:任务在新开启的子线程中执行,具备开启新线程的能力
  • 队列:可以理解为是控制多个任务的执行顺序

    • 串行:多个任务一个挨着一个的有序执行,上一个任务执行完接着执行下一个任务
    • 并发:多个任务并发(同时)执行,会自动开启多个线程来同时执行任务

总结如下图:

iOS多线程之GCD_第2张图片
image
iOS多线程之GCD_第3张图片
image
iOS多线程之GCD_第4张图片
image

接下来我们先用代码验证同步执行任务的情况,代码如下:

同步串行

void syncSerialTest() {
    // 创建一个串行队列,一个挨着一个的执行任务
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    
    // 同步执行任务,在当前主线程上执行任务,没有开启新线程(不具备开启异步线程的能力)
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务1+++顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务2---顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
}

同步并发

void syncConcurrentTest() {
    // 创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 同步执行任务,在当前主线程上执行任务,没有开启新线程(不具备开启异步线程的能力)
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务1+++顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务2---顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
}

异步串行

void asyncSerialTest() {
    // 创建一个串行队列,一个挨着一个的执行任务
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    
    // 异步执行任务
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务1+++顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务2---顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
}

异步并发

void asyncConcurrentTest() {
    // 创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 异步执行任务
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务1+++顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务2---顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
}

从上面的测试代码打印我们可以看到,当我们使用dispatch_snyc同步执行任务,任务在当前主线程执行,没有开启新的子线程,不管是串行队列还是并发队列,最终任务都是一个挨一个的串行执行

当我们使用dispatch_async异步执行任务时,任务会在新开启的子线程中执行,如果是串行队列,多个任务还是一个挨一个的串行执行,如果是并发队列,则此时多个任务是并发同时执行

注意:当我们使用dispatch_async异步执行任务,但是此时的队列如果是dispatch_get_main_queue主队列,则此时并没有开启新的子线程,任务任然是在当前主线程中执行,代码如下:

// 异步主队列(dispatch_get_main_queue是一种特殊的串行队列)
void asyncMainQueue() {
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务1---顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
    
    dispatch_queue_t queue2 = dispatch_get_main_queue();
    dispatch_async(queue2, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"执行任务2---顺序%ld-%@", (long)i, [NSThread currentThread]);
        }
    });
}

总结如图:

iOS多线程之GCD_第5张图片
image

我们再来看一个使用dispatch_sync造成线程死锁的示例,代码如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    NSLog(@"任务1");
    
    // 主队列,串行队列有FIFO特点,也就是先进先出
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 同步执行任务,立即在当前主线程执行任务,任务不执行完后面的代码就会不会执行
    dispatch_sync(queue, ^{
        NSLog(@"任务2");
    });
    
    NSLog(@"任务3");
}

上面的代码就造成了线程死锁,因为dispatch_sync特点是要求任务在当前主线程立即执行完任务2,任务2不执行完后面的代码就不会执行,但是当前线程任务3还没有执行完,不能够执行任务2,这样就导致了任务2等任务3执行完,任务3等任务2执行完,产生死锁

注意:串行队列有FIFO特点,在本示例中,执行viewDidLoad函数也是当前线程的一个任务,是在任务2之前进入到队列中排队执行的任务

如图:

iOS多线程之GCD_第6张图片
image

我们将上面的dispatch_sync同步执行任务改为dispatch_async异步执行任务就不会产生死锁,示例代码如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
   NSLog(@"任务1");
   
   // 主队列,串行队列有FIFO特点,也就是先进先出
   dispatch_queue_t queue = dispatch_get_main_queue();
   
   // 异步执行任务,不要求任务立即执行
   dispatch_async(queue, ^{
       NSLog(@"任务1");
   });
   
   NSLog(@"任务3");
}

接下来我们再来看下线程组dispatch_group_t的基本用法,假设我们需要实现下面这个需求:异步并发执行任务1和任务2,等任务1和任务2都执行完后在回到主线程执行任务3,这里就可以借助线程组来实现,代码如下:

    // 创建线程组
    dispatch_group_t group = dispatch_group_create();
    
    // 创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 添加任务1到group
    dispatch_group_async(group, queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            NSLog(@"任务1 == %zd ---%@",i, [NSThread currentThread]);
        }
    });
    
    // 添加任务2到group
    dispatch_group_async(group, queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            NSLog(@"任务2 == %zd ---%@",i, [NSThread currentThread]);
        }
    });
    
    // 等group前面的任务都执行完后,再执行notify中的任务3
    dispatch_group_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            for (NSInteger i = 0; i < 5; i ++) {
                NSLog(@"任务3 == %zd ---%@",i, [NSThread currentThread]);
            }
        });
    });

讲解示例Demo地址:https://github.com/guangqiang-liu/09-GCD

更多文章

  • ReactNative开源项目OneM(1200+star):https://github.com/guangqiang-liu/OneM:欢迎小伙伴们 star
  • iOS组件化开发实战项目(500+star):https://github.com/guangqiang-liu/iOS-Component-Pro:欢迎小伙伴们 star
  • 主页:包含多篇iOS和RN开发相关的技术文章http://www.jianshu.com/u/023338566ca5 欢迎小伙伴们:多多关注,点赞
  • ReactNative QQ技术交流群(2000人):620792950 欢迎小伙伴进群交流学习
  • iOS QQ技术交流群:678441305 欢迎小伙伴进群交流学习

你可能感兴趣的:(iOS多线程之GCD)