GCD
GCD是纯C语言的。
串行队列(DISPATCH_QUEUE_SERIAL
):一次只能执行一个任务,队列中的任务按顺序执行,一个接一个,像排队跑步一样,保持队形
并行队列(DISPATCH_QUEUE_CONCURRENT
):可以同时执行多个任务,像并排跑,类似于赛跑
同步执行(sync):不具备开启新线程的能力,任务创建后就要执行完才能继续往下走
异步执行(async):具备开启新线程的能力,任务创建后可以先绕过,回头再执行
|
|
|
|
队列和任务组合
并行队列 | 串行队列 | 主队列 | |
---|---|---|---|
异步执行 | 开启多个新线程,任务同时执行 | 开启一个新线程,任务顺序执行 | 不开启新线程,任务顺序执行 |
同步执行 | 不开启新线程,任务顺序执行 | 不开启新线程,任务顺序执行 | 死锁 |
同步任务嵌套
并行队列 | 串行队列 | |
---|---|---|
同步嵌套异步 | 开启新线程,不阻塞 | 开启新线程,不阻塞 |
同步嵌套同步 | 不开启新线程,不阻塞 | 阻塞 |
GCD常见用法和应用场景
Main Dispatch Queue
主队列,是串行队列,一般用于更新界面,跟NSObject类的performSeletorOnMainThread: 方法有相同的作用
|
|
Global Dispatch Queue
全局队列,是并行队列,所有的应用程序都可以使用该并行队列,没必要自已创建并行队列,只需要获取该队列即可
|
|
dispatch_set_target_queue
设置一个队列的优先级。
手动创建的队列,无论是串行队列还是并发队列,都跟默认优先级的全局并发队列具有相同的优先级。
DISPATCH_QUEUE_PRIORITY_HIGH
:高DISPATCH_QUEUE_PRIORITY_DEFAULT
: 默认DISPATCH_QUEUE_PRIORITY_LOW
:低DISPATCH_QUEUE_PRIORITY_BACKGROUND
:后台
dispatch_after
延迟执行一个任务,在x秒后把任务追加到队列中,并不是在x秒后执行。
|
|
dispatch_group & dispatch_group_notify
调度组,可以把多个队列、多个任务都放到同一个group里面,当group的所有任务都完成了,Dispatch可以异步或者同步通知你。
有些时候,我们会有这样子的需求,执行多种操作,当这些操作都完成的时候,再更新UI,比如多图片下载。如果没有group,你就需要自已去统计哪个操作完成了,但有了 dispatch_group
一切将简化。
|
|
dispatch_group_enter & dispatch_group_leave
进入和离开调度组。
dispatch_group_wait
等待调度组完成所有任务。
如果group的所有任务都执行完毕,它会返回 0 ,如果超时,则会返回一个不为 0 的long类型的值。
dispatch_group_wait
第一个参数为需要等待的目标调度组,第二个参数则是等待的时间,DISPATCH_TIME_FOREVER
,表示永远等待。
|
|
如果等待时间是 DISPATCH_TIME_FOREVER
,最后两句需要5秒,才可以执行完毕,执行结果是:
|
|
如果把等待时间改为4秒,则第3个block需要5秒才能执行完毕,所以会超时,执行结果是:
|
|
dispatch_barrier_async
栅栏函数,它就好像栅栏一样可以将多个任务分隔开,在它前面追加的任务先执行,然后执行它自己的任务,然后在它后面追加的任务才执行。
注意:
dispatch_barrier_async
中传入的参数队列必须是由dispatch_queue_create
方法创建的队列,否则,与dispatch_async
无异,起不到“栅栏”的作用了,对于dispatch_barrier_sync
也是同理。
|
|
执行结果:
|
|
dispatch_barrier_sync
作用于 dispatch_barrier_async
一样
不同点:
dispatch_barrier_sync
将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们。dispatch_barrier_async
将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务。
dispatch_apply
重复执行同一个任务。
|
|
执行结果:
|
|
dispatch_one
保证在App生命周期内只执行一次任务且线程安全,比如单例模式。
|
|
dispatch_suspend & dispatch_resume
dispatch_suspend
: 挂起指定的队列dispatch_resume
: 恢复指定队列
|
|
dispatch_semaphore_signal
信号量,如果信号量计数大于或等于1,那个允许线程执行,如果信号量当前计数为0,线程将阻塞。
创建信号量
|
|
发出等待信号(信号量计数减1)
|
|
发出通行信号 (信号量计数加1)
|
|
|
|
NSOperation
NSOperation
不可以直接创建,其实用的比较多的是 NSOperationQueue
和 它的两个子类 NSBlockOperation
和 NSInvocationOperation
(Swift 不支持)。
NSOperationQueue
NSOperationQueue
底层是基于 GCD 的封装,queue 会根据 operation 的优先级、依赖等来决定如何执行添加进来的操作。所有的自定义队列,都是在子线程中执行的。
创建一个线程队列
|
|
获取main队列
|
|
NSBlockOperation
NSBlockOperation
是 NSOperation
的子类,用于管理一个或多个 block 的并发执行,会开启一个或多个线程,这些线程都由 queue 自己管理。可以通过设置依赖来控制执行顺序。
|
|
执行结果:
|
|
NSInvocationOperation
NSInvocationOperation
是 NSOperation 的子类,用于管理调用单个封装任务的执行,此类实现非并发操作。
|
|
进阶
NSOperation 有四种状态:
isReady
: 表示操作是否已经准备好被执行。isExecuting
: 表示操作是否正在执行。isFinished
: 表示操作是否执行成功了。isCancelled
: 表示操作是否被取消了。
cancel
NSOperation 的 - (void)cancel
方法作用是:建议操作对象停止执行任务。
此方法不会强制停止操作,而是更新对象的内部标志以反映状态的变化,但取消尚未执行的操作可以使该操作更早地从队列中删除。
- 如果操作已经完成,则此方法无效。
- 如果某个操作处于队列中,但正在等待未完成的依赖操作,则会从队列中删除该操作。
- 如果取消不在队列中的操作,则此方法会立即将对象标记为已完成。
CompletionBlock
每个 NSOperation 执行完毕之后,就会执行该block,提供了一个非常好的方式让你能在 View Controller 或者 Model里加入自己更多自己的代码逻辑。比如说,你可以在一个网络请求操作的completionBlock来处理操作执行完以后从服务器下载下来的数据。
|
|
手动调用 operation
待补充…
自定义同步的 operation
待补充…
自定义异步的 operation
待补充…
NSThread
NSThread 是基于线程使用的、轻量级的多线程编程方法(相对GCD和NSOperation),一个NSThread对象代表一个线程,需要手动管理线程的生命周期,处理线程同步等问题。一般不推荐使用。
有两种创建方式:
|
|
|
|
获取当前线程
|
|
获取主线程
|
|
线程操作
开启线程
|
|
暂停线程
|
|
取消线程
|
|
终止线程
|
|
应该避免调用此方法,因为它不会让你的线程有机会清理在执行过程中分配的任何资源,可能会导致内存问题。
设置优先级
有以下几种优先级:
- NSQualityOfServiceUserInteractive:最高优先级,用于用户交互事件
- NSQualityOfServiceUserInitiated:次高优先级,用于用户需要马上执行的事件
- NSQualityOfServiceDefault:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
- NSQualityOfServiceUtility:普通优先级,用于普通任务
- NSQualityOfServiceBackground:最低优先级,用于不重要的任务
|
|
执行
|
|
参考: