这是一篇我关于 GCD 的使用以及学习的总结文章。持续更新。
感谢诸多大神在此之前写的各类文章,如果可以,我会尽量把他们添加到附录中,如有遗漏,感谢提醒补充和讨论。
2016-08-06
成果
假如你对我的废话没兴趣,那么看到这里就够了。这是我存在 GitHub 上的代码文件,假如链接失效,你可以给我留言。
GCD.swift
为何 GCD
我在工作中经常使用 GCD,因为我感觉这样的代码更加直接了当,如果不是必要情况,我甚至不愿意使用 NSThread,NSOperationQueue,当然,有时候,这是一种非常折腾的癖好。
总之,这些都是多线程管理工具,如果你有兴趣,简书上有非常简单易懂的资料。稍微搜索一下即可。
至于好处,我只说一点。
这是 iOS 提供的最底层的多线程接口。
基础概念
--> 名词解释
- Block 任务:具体要执行的工作内容
- Queue 队列:一个用于放置 Block 的队伍,一个 Queue 中会有多个 Block。
- Concurrent 并发:Queue 执行自己的 Block 的方式,并发表示会有多个 Block 同时运行。
- Serial 串行:Queue 执行自己的 Block 的方式,串行表示所有 Block 会依次按顺序一个运行完才运行下一个。
- sync 同步:不会开启新线程,堵塞当前线程来执行
- async 异步:开启新线程,不堵塞当前线程
- Thread 线程:程序中执行代码的通道,Queue 会把 Block 传递到这里来进行运行。
-
Process 进程:一个程序为一个进程,一个运行的 App 即是 iOS 系统当中运行的一个进程,一个进行可以有多个线程。
--> GCD 变量
---> dispatch_queue_t:队列
func TestGCD() {
var queue: dispatch_queue_t
// 创建串行队列
queue = dispatch_queue_create("A Serial Queue", DISPATCH_QUEUE_SERIAL)
// 创建并行队列
queue = dispatch_queue_create("A Concurrent", DISPATCH_QUEUE_CONCURRENT)
// 获取程序主队列
queue = dispatch_get_main_queue()
// 获取程序全局队列
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
}
---> dispatch_source_t: GCD 基础数据类型
官方解释:GCD provides a suite of dispatch sources—interfaces for monitoring (low-level system objects such as Unix descriptors, Mach ports, Unix signals, VFS nodes, and so forth) for activity. and submitting event handlers to dispatch queues when such activity occurs. When an event occurs, the dispatch source submits your task code asynchronously to the specified dispatch queue for processing.
翻译:GCD 提供一个系列的调度源接口用来监听活动(底层的系统对象:例如 Unix 描述符,Mach 端口,Unix 信号,VFS 节点等等)。并且在活动发生的时候发送事件句柄给调度的队列。当一个事件发生的时候,发送源派遣你的任务代码到指定的派遣队列中异步执行。
--> GCD 方法
---> 基础方法
----> dispatch_sync(queue: dispatch_queue_t, block: dispatch_block_t)
func TestGCD() {
// dispatch_sync(queue: dispatch_queue_t, block: dispatch_block_t)
// 同步执行方法,会堵塞当前的线程,然后把该任务完成后才继续当前线程。
// 注意:如果在 main_queue 中 dispatch_sync(queue: main_queue, block: {}) 会导致死锁
print("Test Start in \(NSThread.currentThread()).")
for i in 0 ..< 10 {
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
print("Test \(i) Run in \(NSThread.currentThread()).")
}
}
print("Test End in \(NSThread.currentThread()).")
}
/* 运行结果
Test Start in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 0 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 1 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 2 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 3 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 4 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 5 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 6 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 7 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 8 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 9 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test End in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
*/
----> dispatch_async(queue: dispatch_queue_t, block: dispatch_block_t)
func TestGCD() {
// dispatch_async(queue: dispatch_queue_t, block: dispatch_block_t)
// 异步执行方法,不会堵塞当前的线程,但是运行顺序将不可控。
print("Test Start in \(NSThread.currentThread()).")
for i in 0 ..< 10 {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
print("Test \(i) Run in \(NSThread.currentThread()).")
}
}
print("Test End in \(NSThread.currentThread()).")
}
/* 运行结果
Test Start in <NSThread: 0x7fbe33c09bf0>{number = 1, name = main}.
Test End in <NSThread: 0x7fbe33c09bf0>{number = 1, name = main}.
Test 2 Run in <NSThread: 0x7fbe33e0b360>{number = 2, name = (null)}.
Test 0 Run in <NSThread: 0x7fbe33f018d0>{number = 3, name = (null)}.
Test 8 Run in <NSThread: 0x7fbe33e0b360>{number = 2, name = (null)}.
Test 1 Run in <NSThread: 0x7fbe33e0cf90>{number = 4, name = (null)}.
Test 3 Run in <NSThread: 0x7fbe33f05c90>{number = 5, name = (null)}.
Test 4 Run in <NSThread: 0x7fbe33f05880>{number = 6, name = (null)}.
Test 5 Run in <NSThread: 0x7fbe33d1dc60>{number = 7, name = (null)}.
Test 6 Run in <NSThread: 0x7fbe33d09660>{number = 8, name = (null)}.
Test 7 Run in <NSThread: 0x7fbe33f04cf0>{number = 9, name = (null)}.
Test 9 Run in <NSThread: 0x7fbe33f018d0>{number = 3, name = (null)}.
*/
---> dispatch_source_t 方法
----> dispatch_source_t dispatch_source_create( dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)
Creates a new dispatch source to monitor low-level system objects and automatically submit a handler block to a dispatch queue in response to events.
创建一个新的调度源来监听底层系统对象并在响应事件的时候自动提交处理块给派遣队列。
- type:源处理事件类型
- DISPATCH_SOURCE_TYPE_DATA_ADD:自定义的事件,变量增加
- DISPATCH_SOURCE_TYPE_DATA_OR:自定义的事件,变量OR
- DISPATCH_SOURCE_TYPE_MACH_SEND:MACH端口发送
- DISPATCH_SOURCE_TYPE_MACH_RECV:MACH端口接收
- DISPATCH_SOURCE_TYPE_PROC:进程监听,如进程的退出、创建一个或更多的子线程、进程收到UNIX信号
- DISPATCH_SOURCE_TYPE_READ:IO操作,如对文件的操作、socket操作的读响应
- DISPATCH_SOURCE_TYPE_SIGNAL:接收到UNIX信号时响应
- DISPATCH_SOURCE_TYPE_TIMER:定时器
- DISPATCH_SOURCE_TYPE_VNODE:文件状态监听,文件被删除、移动、重命名
- DISPATCH_SOURCE_TYPE_WRITE:IO操作,如对文件的操作、socket操作的写响应
- handle:句柄、索引或id,假如要监听进程,需要传入进程的ID
- mask:可以理解为描述,提供更详细的描述,让它知道具体要监听什么
- queue:自定义源需要的一个队列,用来处理所有的响应句柄(block)
// 在主队列中创建一个定时器
var timer: dispatch_source_t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue())
// 创建 计时器 dispatch_source_t
var source: dispatch_source_t? = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue())
// 计数
var sourceTimes = 10
//
func TestGCD() {
// 设置 dispatch_source_t 为从现在开始每秒调用一次
dispatch_source_set_timer(source!, dispatch_walltime(nil, 0), NSEC_PER_SEC, 0)
// 设置调用时的处理
dispatch_source_set_event_handler(source!) {
// 打印当前线程,以及计数值,然后减1.
print("\(self.sourceTimes): \(NSThread.currentThread())")
self.sourceTimes -= 1
// 如果计数结束则 释放 或 cancel, 都会导致停止,但是释放不会调用 cancel handler
if self.sourceTimes <= 0 {
//self.source = nil
dispatch_source_cancel(self.source!)
}
}
// 调用 cancel 的时候的处理
dispatch_source_set_cancel_handler(source!) {
print("source Cancel: \(NSThread.currentThread())")
self.source = nil
}
// 启动 source 监听
dispatch_resume(source!)
}
----> void dispatch_source_set_timer( dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway) 设置计时器时间
Sets a start time, interval, and leeway value for a timer source.
设置计时器源的起始时间、 间隔和回旋余地值。
----> dispatch_time_t dispatch_walltime( const struct timespec *when, int64_t delta)
Creates a dispatch_time_t using an absolute time according to the wall clock.
根据挂钟来创建一个绝对事件。
- when:A struct timespec to add time to. If NULL is passed, then this function uses the result of gettimeofday.
- delta:Nanoseconds to add.
----> dispatch_time_t dispatch_time( dispatch_time_t when, int64_t delta);
Parameters
Creates a dispatch_time_t relative to the default clock or modifies an existing dispatch_time_t.
根据默认的时钟创建一个 dispatch_time_t 或修改一个现有的 dispatch_time_t
- when:The dispatch_time_t value to use as the basis for a new value. Pass DISPATCH_TIME_NOW to create a new time value relative to now.
- delta:The number of nanoseconds to add to the time in the when parameter.
参考文献
- 黑幕居士 -> iOS编程--GCD相关(进程、线程/多线程、并行/串行队列、同步/异步任务)
- John_LS -> IOS dispatch source 学习篇
- 未之 -> iOS多线程——Dispatch Source
我的 GitHub 空间:Myron Work Space