GCD探究


要了解GCD(Grand Central Dispatch),先从最基本的一些概念作为切入点

1.概念理解

进程

只指在系统中正在运行的一个应用程序,比如同时打开了Xcode和Sourcetree, 系统会分别启动两个进程,Mac上有个活动监视器,可以查看已打开的进程并可以杀死进程。每个进程之间是独立的,均运行在其专用且受保护的内存空间内


线程

进程想要执行任务,必须得有线程,每个进程至少要有一条线程,一个进程所有的任务都在线程间执行


线程的串行

一个线程中任务的执行是串行的,如果要在一个线程中执行多个任务,那么只能一个一个的按顺序执行这些任务,也就是说,在同一时间内,一个线程只能执行一个任务


多线程

一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务,多线程可以提升程序的工作效率。其实,在同一时间,CPU只能处理一条线程,只有一条线程在工作,多线程并发,只是CPU在快速的在多条线程之间调度。如果同时执行的线程过多,反而会拖慢程序的执行效率,因为切换线程也同样需要消耗资源,每条线程被调度的频次也会降低,所以多线程也并非是越多越好


多线程的优缺点

可以适当提高程序的执行效率和资源利用率。但是创建线程也是有开销的,以iOS为例,成本主要包括:内核数据结构(大约1kb),栈空间(子线程512KB,主线程1MB,也可以通过setStackSize设置,但必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的时间。如果开启大量线程,会降低程序性能,线程越多,CPU在地调度线程上的开销就越大,也有可能会导致程序设计更加复杂,比如线程之间通信、数据共享等


主线程

iOS程序运行后,默认会开启一条线程,称为主线程或UI线程,用来显示刷新UI界面,处理一些点击、滚动或拖拽等UI事件,但是尽量不要将比较耗时的操作放到主线程,否则会卡主主线程,影响UI流畅度

2.多线程实现

技术方案 简介 语言 线程声明周期 使用频率
pthread 1.一套通用的多线程API
2.适用于Unix\Linux\Windows等系统
3.跨平台\可移植
C 程序员管理 几乎不用
NSThread 1.使用更加面向对象
2.简单易用,可直接操作县城对象
OC 程序员负责创建 偶尔使用
GCD 1.旨在替代NSThread等等多核技术
2.充分利用设备的多核
C 自动管理 经常使用
NSOperation 1.基于GCD
2.比GCD多了一些更简单的实用功能
3.使用更加面向对象
OC 自动管理 经常使用
  • pthread

在查看CPU的使用上也是可以直观看到是有多条线程在同时执行

  • NSThread

一个NSThread对象就代表一条线程,线程一旦死亡不能再开启任务

线程状态

多线程的安全隐患

多个线程访问同一块资源可能发生安全隐患

这个时候就需要加锁,即互斥锁

锁定一段代码只能使用一把锁,使用多把锁匙无效的,互斥锁能有效防止因多线程抢夺同一块资源而造成的数据安全问题,但是却需要消耗大量的CPU资源,注意,在主线程加锁是没有意义的

OC在定义属性的时候有nonatomicatomic,即非原子属性和原子属性,默认是原子属性。原子属性,为setter方法加锁,非原子属性则不会加锁 。但是虽然atomic线程安全,却需要消耗大量的资源。nonatomatic非线程安全,适合内存小的移动设备。所以我们iOS开发,建议所有属性都声明为nonatomatic,在需要线程安全的地方再手动加锁

在一个进程中中,线程往往不是孤立存在的,多个线程之间经常需要通信,经典的图片下载显示就需要线程间的通信,线程间通信通常可以使用performSelector,还可以使用NSPortNSMessagePortNSMachPort等端口,不过使用不多

  • GCD

Grand Central Dispatch,很⑥的中枢调度器,纯C语言,提供了很多非常强大的函数

GCD的优势

  • GCD是🍎公司为多核的并行运算提出的解决方案

  • GCD会自动利用更多的CPU内核(双核、四核)

  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

  • 攻城狮只需要告诉GCD要执行什么任务,不需要编写任何线程管理代码

GCD的两个核心概念

  • 任务: 执行什么操作

  • 队列: 用来存放任务

GCD的使用

  • 定制任务

    • 确定想做的事情
  • 将任务添加到队列中

    • GCD会自动将队列中的任务取出,放到对应的线程中执行

    • 任务的取出遵循队列的FIFO原则:先进先出,后进后出

GCD中有两个用来执行任务的常用函数

  • 同步 diapatch_sync(dispatch_queue_t queue, dispatch_block_t block)

    • queue: 队列

    • block: 任务

  • 异步 dispatch_async(dispatch_queue_t queue,dispatch_block_t block)

注意,同步只能在当前线程中执行任务,不具备开启新线程的能力
异步可以在新的线程中执行任务,具备开启新线程的能力

GCD的队列类型

  • 并发队列(Concurrent Dispatch Queue

    • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

    • 并发功能只有在异步(dispatch_async)函数下才有效

  • 串行队列

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

上面提到的几个术语容易混淆:同步、异步、并发、串行

  • 同步和异步主要影响:能不能开启新的线程
    同步: 只是在当前线程中执行任务,不具备开启新线程的能力

    异步: 可以在新的线程中执行任务,具备开启新线程的能力

  • 并发和串行的主要影响: 任务的执行方式

    • 并发:多个任务并发(同时)执行

    • 串行:一个任务执行完毕后,再执行下一个任务

GCD的各种队列

并发队列 手动创建的串行队列 主队列
同步(sync) 没有开启新线程
串行执行任务
同左 同左
异步 (async) 有开启新线程
并发执行任务
有开启新线程
串行执行任务
没有开启新线程
串行执行任务

注意,使用sync函数往当前串行队列中添加任务,会卡住当前串行队列

线程间通信

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEF    AULT, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^{
        });
//        dispatch_sync(dispatch_get_main_queue(), ^{
//        });
        NSLog(@"");
    });

GCD中常用函数

dispatch_barrier_async(dispatch_queue_t queue,dispatch_block_t block)

在前面任务结束后他才执行,而且它后面的任务等它执行完成后才会执行, 这个queue不能是全局的并发地列,需手动创建

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ })

延迟执行, 不一定非要放到主线程

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});

一次性代码,该函数能保证某段代码在程序运行过程中只被执行一次

// .h
#define FNSingletonH + (instancetype)sharedInstance;

// .m
#define FNSingletonM \
static id _instace; \
 \
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instace = [super allocWithZone:zone]; \
    }); \
    return _instace; \
} \
 \
+ (instancetype)sharedInstance \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instace = [[self alloc] init]; \
    }); \
    return _instace; \
} \
 \
- (id)copyWithZone:(NSZone *)zone \
{ \
    return _instace; \
}

常用于单例模式,可以保证在程序运行过程中,一个类只有一个实例,而且该实例易于供外界访问,从而方便控制了实例个数又节约系统资源

dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t      _Nonnull queue#>, <#^(size_t)block#>)

快速迭代遍历

 dispatch_group_t group = dispatch_group_create(); 
 dispatch_queue_t queue =     dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_async(group, queue, ^{
   });
   dispatch_group_async(group, queue, ^{
 });
dispatch_group_notify(group, queue, ^{
   });

队列组,等二个异步操作都执行完毕后,再回到指定线程操作



未完待续…


如有任何疑问或问题请联系我:fishnewsdream@gmail.com,欢迎交流,共同提高!

Objective-C/Swift技术开发交流群201556264,讨论何种技术并不受限,欢迎各位大牛百家争鸣!

微信公众号OldDriverWeekly,欢迎关注并提出宝贵意见

老司机iOS周报,欢迎关注或订阅

刚刚在线工作室,欢迎关注或提出建设性意见!

刚刚在线论坛, 欢迎踊跃提问或解答!

如有转载,请注明出处,谢谢!

本站总访问量 本文总阅读量