iOS 多线程: 初步认识
几乎每个编程语言或者平台都会遇到多线程的问题, 说明多线程是一个非常重要且开发者必须了解和掌握的.
多线程也是面试官比较喜欢问的问题, 例如:
- 进程和线程的区别, Android 是否支持多进程?
- 线程池如何实现的?
- 锁机制?
- 多线程之间如何通信?
谈及 iOS 中的多线程,一般说的是 pthread,NSthread,GCD,NSOperation 这四种, 用的最多也最方便的就是 GCD 了. 关于这四者, 后续会为大家一一分享.
phtread 是跨平台的, C/C++ 中都有它的声影, GCD 和 NSOperation 都是常用的,NSOperation 是基于 GCD 的. GCD 的核心概念是将一个任务添加到队列,指定任务执行的方法,然后执行, NSOperation 则是直接将一个操作添加到队列中.
该系列文章来跟大家分享关于 iOS 中的多线程.
- iOS 多线程: 初步认识(本篇)
进程和线程
进程和线程的定义, 大家可以自行到维基百科上面去查.
这里只说二者的区别.
一个程序至少有一个进程, 一个进程至少有一个线程如主线程.
多线程程序的并发性高.
进程在执行过程中拥有独立的内存单元,而多线程是共享内存的,从而极大地提高了程序的运行效率.
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口. 线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制.
操作系统并没有将多个线程看做多个独立的应用, 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行, 从而实现了进程的调度和管理以及资源分配.
队列
iOS 中, 队列主要分为:
- 全局队列
- 主队列.
- 串行队列.
- 并发队列.
- Concurrent:
tasks are dequeued in FIFO order, but run concurrently and can finish in any order.
- Serial: .
tasks execute one at a time in FIFO order
并发: 任务以 FIFO 从序列中移除,然后并发运行,可以按照任何顺序完成.
串行: 任务以FIFO从序列中一个一个执行. 一次只调度一个任务.
在 iOS 中, 并发不一定会开启多个线程, 串行也不一定只开启一个线程. 因为这里会牵扯到是异步还是同步执行.
主队列, 即 mainQueue.
Returns the default queue that is bound to the main thread.
会关联主线程.
全局队列, 即 globalQueue.
The well-known global concurrent queues may not be modified.
全局队列中执行不一定会开启新线程.
同步和异步
在 iOS 的 GCD 中, 还有同步和异步执行的区别.
同步
同步执行代码块, 诸如 dispatch_async 中执行的.
异步
异步执行代码块, 诸如 dispatch_sync 中执行的.
创建队列的方法
创建主队列
1 | dispatch_queue_t dispatch_get_main_queue(void); |
创建全局队列
1 | dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags); |
参数 identifier 用来表示优先级. 对应的优先级为:
- DISPATCH_QUEUE_PRIORITY_HIG
- DISPATCH_QUEUE_PRIORITY_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW
- DISPATCH_QUEUE_PRIORITY_BACKGROUND
如果传入 0 标示 DISPATCH_QUEUE_PRIORITY_DEFAULT.
参数 flags 是一个保留参数, API 文档要求传入 0, 非0值可能会导致返回结果为 NULL.
自定义队列
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
参数 label 是一个字符串.
参数 attr 用来标示是串行还是并行队列. 可以从 DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_CONCURRENT 二者中取值.
如果该参数传入 NULL, 默认是 DISPATCH_QUEUE_SERIAL 串行队列.
串行队列中的线程
串行队列到底是开了一个线程, 还是开了多个线程, 我们一探究竟.
先看例子1
1 | dispatch_queue_t serialQueue = dispatch_queue_create("com.veryitman", DISPATCH_QUEUE_SERIAL); |
可以看出, 只开启一个新线程.
再看例子2
1 | dispatch_queue_t serialQueue = dispatch_queue_create("com.veryitman", DISPATCH_QUEUE_SERIAL); |
可以看出, 也只开启一个新线程.
改造一下例子2, 将其中的一个异步改为同步
1 | dispatch_queue_t serialQueue = dispatch_queue_create("com.veryitman", DISPATCH_QUEUE_SERIAL); |
可以看出, 同步的执行在主线程, 二者并不是在一个线程中执行.
所以, 串行队列中执行的代码, 不一定都在子线程中, 如果是异步都是同一个线程中执行.如果是同步的话, 会在主线程中执行.
同理, 并行队列中的异步执行会开启多个线程来执行.
执行方式和队列
这里的执行方式指的是同步或者异步执行.
先看异步执行
1 | /// 异步全局队列和主队列 |
再看同步执行
1 | /// 同步全局队列和主队列 |
得出结论
- 任何队列的同步执行, 都没有开启新线程, 在主线程中执行.
- 主队列的同步执行会造成死锁.
- 主队列的异步执行, 没有开启新线程. 在主线程中执行.
- 串行和并行以及全局队列的异步执行, 都会开启新线程.