要了解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在定义属性的时候有nonatomic和 atomic,即非原子属性和原子属性,默认是原子属性。原子属性,为setter方法加锁,非原子属性则不会加锁 。但是虽然atomic线程安全,却需要消耗大量的资源。nonatomatic非线程安全,适合内存小的移动设备。所以我们iOS开发,建议所有属性都声明为nonatomatic,在需要线程安全的地方再手动加锁
在一个进程中中,线程往往不是孤立存在的,多个线程之间经常需要通信,经典的图片下载显示就需要线程间的通信,线程间通信通常可以使用performSelector,还可以使用NSPort、NSMessagePort、NSMachPort等端口,不过使用不多
- 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周报,欢迎关注或订阅
刚刚在线工作室,欢迎关注或提出建设性意见!
刚刚在线论坛, 欢迎踊跃提问或解答!
如有转载,请注明出处,谢谢!