iOS-GCD深度解析

2020-01-03  本文已影响0人  LZZ_IOS

一、GCD的优势

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

二、GCD的重要概念

GCD的两个核心概念:任务与队列

1、任务:

执行什么操作,执行任务有两种方式:同步执行函数和异步执行函数,他们之间的区别在于

1.1、同步函数:

只能在当前线程中执行任务,不具备开辟线程的能力,任务立刻执行,会阻塞当前线程并等待Block中的任务执行完毕,然后当前线程才能继续往下执行;

1.2、异步函数:

具备开辟线程的能力,但不一定会开辟新线程,当前线程会直接往下执行,不会等待Block中的任务执行完毕,所以不会阻塞当前线程;

2、队列:

用来存放任务,分为串行队列与并行队列

2.1、串行队列

让任务一个接一个执行(一个任务执行完毕之后,才能执行下一个任务);

2.2、并发队列

可以同时执行多个任务(自动开启多个线程同时执行任务),并发队列只有在异步函数(dispatch_async)才有效(同步函数不具备开辟线程的能力);

三、GCD使用

1、GCD使用步骤

定制任务(确定想做的事情),将任务添加到队列中(GCD会自动将队列中的任务取出,放在对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出);

2、创建队列

创建队列

//第一个参数<#const char * _Nullable label#>: C语言字符串,唯一标识
//第二个参数<#dispatch_queue_attr_t  _Nullable attr#>: 队列的类型
// 并行队列: DISPATCH_QUEUE_CONCURRENT
// 串行队列: DISPATCH_QUEUE_SERIAL 或者 NULL
dispatch_queue_t queue = dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t  _Nullable attr#>);

创建并行队列

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

创建串行队列

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

GCD默认已经提供了全局并行队列,供整个应用使用,可以无需手动创建

//第一个参数:优先级
//#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 //后台
//第二个参数:预留参数 0
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

获取主队列

dispatch_queue_t queue = dispatch_get_main_queue();

3、任务执行

队列在queue中,任务在block块中
开启同步函数 同步函数:会阻塞当前线程,没有开辟线程的能力

dispatch_sync(queue,^{
  
});

开启异步函数 异步函数:不会阻塞当前线程,会开辟线程执行任务

dispatch_async(queue,^{
  
});

4、任务和队列组合

任务:同步函数 异步函数
队列:串行 并行

异步函数+并发队列:会开辟新的线程,并发执行

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue,^{
  NSLog(@"---%@---",[NSThread currentThread]);
});

异步函数+串行队列:只会开辟一条线程,任务串行执行

dispatch_queue_t queue =  dispatch_queue_create("com.lz", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
       NSLog(@"---%@---",[NSThread currentThread]);
 });

同步函数+并行队列:不会开辟线程,任务串行执行

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_sync(queue,^{
  NSLog(@"---%@---",[NSThread currentThread]);
});

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

dispatch_queue_t queue =  dispatch_queue_create("com.lz", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
   NSLog(@"---%@---",[NSThread currentThread]);
 });

主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放在主线程中执行
异步函数+主队列:不会开辟线程,任务串行执行

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue,^{
  NSLog(@"---%@---",[NSThread currentThread]);
});

同步函数+主队列:死锁

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue,^{
  NSLog(@"---%@---",[NSThread currentThread]);
});

因为主队列是串行队列,任务只能一个一个执行。而在主队列中添加同步任务,则会造成主队列等待同步任务执行完成,才能继续执行主队列任务,因此就会相互等待而发生死锁。将这个方法放入子线程中,则不会发生死锁,任务串行执行;

5、任务队列组合总结

同步函数和异步函数的执行顺序
同步函数:立刻马上执行,会阻塞当前线程

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSLog(@"---start---");
dispatch_sync(queue, ^{
    NSLog(@"---11---%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"---22---%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"---33---%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"---44---%@",[NSThread currentThread]);
});
NSLog(@"---end---");

我们看一下输出

---start---
---11---<NSThread: 0x6000022f1340>{number = 1, name = main}
---22---<NSThread: 0x6000022f1340>{number = 1, name = main}
---33---<NSThread: 0x6000022f1340>{number = 1, name = main}
---44---<NSThread: 0x6000022f1340>{number = 1, name = main}
---end---

同步函数会阻塞线程,立即执行
异步函数:当前线程会直接往下执行,不会阻塞当前线程

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSLog(@"---start---");
dispatch_async(queue, ^{
    NSLog(@"---11---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"---22---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"---33---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"---44---%@",[NSThread currentThread]);
});
NSLog(@"---end---");

我们来看一下输入

---start---
---end---
---11---<NSThread: 0x6000020f2180>{number = 5, name = (null)}
---22---<NSThread: 0x6000020e1500>{number = 6, name = (null)}
---33---<NSThread: 0x6000020de780>{number = 3, name = (null)}
---44---<NSThread: 0x6000020d6480>{number = 4, name = (null)}

异步函数不会阻塞当前线程
注意:GCD中开多少条线程是由系统根据CUP繁忙程度决定的,如果任务很多,GCD会开启适当的子线程,并不会让所有任务同时执行

6、GCD线程间的通信

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UIImageView *imgV;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self _addSubviews];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSString *imgPath = @"图片地址";
        NSURL *url = [NSURL URLWithString:imgPath];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *img = [UIImage imageWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imgV.image = img;
        });
    });
}

#pragma mark - setter UI
-(void)_addSubviews{
    [self.view addSubview:self.imgV];
}

#pragma mark - getter settr
-(UIImageView *)imgV{
    if (_imgV == nil) {
        _imgV = [[UIImageView alloc] init];
        _imgV.frame = self.view.frame;
    }
    return _imgV;
}
@end

GCD线程间的通信非常简单,使用同步或异步函数,传入主队列即可。

7、GCD中其他常用函数

7.1、栅栏函数(控制任务的执行顺序)
dispatch_barrier_async(queue,^{

});

我们来看一下栅栏函数的作用

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
  [self _barrier];
}
-(void _barrier{
  dispatch_queue_t queue = dispatch_queue_create("com.lz", DISPATCH_QUEUE_CONCURRENT);
  dispatch_async(queue, ^{
      for (NSInteger i = 0; i<3; i++) {
          NSLog(@"%zd-11--%@",i,[NSThread currentThread]);
      }
  });
  dispatch_async(queue, ^{
      for (NSInteger i = 0; i<3; i++) {
          NSLog(@"%zd-22--%@",i,[NSThread currentThread]);
      }
  });
  dispatch_barrier_async(queue, ^{
      NSLog(@"我是一个栅栏函数");
  });
  dispatch_async(queue, ^{
      for (NSInteger i = 0; i<3; i++) {
          NSLog(@"%zd-33--%@",i,[NSThread currentThread]);
      }
  });
  dispatch_async(queue, ^{
      for (NSInteger i = 0; i<3; i++) {
          NSLog(@"%zd-44--%@",i,[NSThread currentThread]);
      }
  });
}

我们来看一下输出

0-22--<NSThread: 0x600001b95600>{number = 4, name = (null)}
0-11--<NSThread: 0x600001bbe2c0>{number = 6, name = (null)}
1-22--<NSThread: 0x600001b95600>{number = 4, name = (null)}
1-11--<NSThread: 0x600001bbe2c0>{number = 6, name = (null)}
2-11--<NSThread: 0x600001bbe2c0>{number = 6, name = (null)}
2-22--<NSThread: 0x600001b95600>{number = 4, name = (null)}
我是一个栅栏函数
0-44--<NSThread: 0x600001bbe2c0>{number = 6, name = (null)}
0-33--<NSThread: 0x600001b95600>{number = 4, name = (null)}
1-44--<NSThread: 0x600001bbe2c0>{number = 6, name = (null)}
1-33--<NSThread: 0x600001b95600>{number = 4, name = (null)}
2-44--<NSThread: 0x600001bbe2c0>{number = 6, name = (null)}
2-33--<NSThread: 0x600001b95600>{number = 4, name = (null)}

栅栏函数可以控制任务执行的顺序,栅栏函数之前的执行完毕之后,执行栅栏函数,然后在执行栅栏函数之后的

7.2、延迟执行
//第一个参数 延时时间
//第二个参数 队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
        
});
7.3、一次性代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
        
});
7.4、倒计时
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(timer, ^{
   
});
dispatch_resume(timer);
7.5、队列组
// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建并行队列 
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 执行队列组任务
dispatch_group_async(group, queue, ^{   
});
//队列组中的任务执行完毕之后,执行该函数
dispatch_group_notify(group, queue, ^{
});

本文借鉴自
Sky109iOS多线程--深度解析

上一篇下一篇

猜你喜欢

热点阅读