这部分内容和OC的GCD几乎没区别;但是先比较OC的一大堆宏定义(DISPATCH_TIME_NOW,NSEC_PER_SEC等等),swift明显更易读一些.
Dispatch Source,调度资源,用于协调处理底层系统事件,在收到指定系统信号的时候,在指定的队列执行任务.
Dispatch Semaphore,用于线程间通信.
一:Dispatch Source
主要有这么几种source:
Timer Source 定时器
File System Source 文件状态监听
Process Source 监听进程状态(ProcessEvent
Memory Pressure Source 内存警告
Signal Source 监听UNIX信号
Mach Port Source 监听Mach Port信号,主要与runloop的应用相关
Custom Source 自定义数据监听
GCD也根据这几种source,提供了对应的一些API.
1.DispatchSourceTimer
DispatchSourceTimer是个协议,它不需要创建类来遵循,而是通过makeTimerSource方法来返回一个对象.
class func makeTimerSource(flags: DispatchSource.TimerFlags = [], queue: DispatchQueue? = nil) -> DispatchSourceTimer
flags一般使用[]或者.strict;queue往那个队列添加任务,设置为nil时会添加到一个全局队列;
func schedule(deadline: DispatchTime, repeating interval: DispatchTimeInterval= .never, leeway: DispatchTimeInterval = .nanoseconds(0))
设置定时器的定时策略:
deadline是定时器执行第一次前的等待时间,也就是从resume()到第一次执行之间的时间;
repeating是每次重复的间隔时间
leeway也是一个延迟,它不作用与第一次执行,表示每次执行可以允许的延迟,用于提升性能,一般不用设置,如果设置了允许的值,则偶尔会出现一点点的偏差
print("开始:\(Date.init().timeIntervalSince1970)")
timer = DispatchSource.makeTimerSource(flags: .strict, queue: .main)
timer?.schedule(deadline: .now() + 3, repeating: 2, leeway: .milliseconds(500))
timer?.setEventHandler {
print("\(Date.init().timeIntervalSince1970)")
}
timer?.resume()
timer需要被持有,如果作为局部变量会被释放,不能执行
设置与不设置leeway的区别看下面两次输出
leeway设置为500毫秒
不设置leeway
func start(){
timer = DispatchSource.makeTimerSource(flags: .strict, queue: .main)
timer?.schedule(deadline: .now() + 2, repeating: .never)
timer?.setEventHandler {
print("\(Date.init().timeIntervalSince1970)")
}
timer?.resume()
}
//取消
func end(){
timer?.cancel()
}
把repeating设置为.never,就会只执行一次,设置deadline来控制延时,这种设置可随时取消执行的延时操作的方法很方便,相较于Dispatchafter需要额外写一些东西来控制,而timer直接cancel()就行,需要的时候再次调用start就行了;
print("start:\(Date.init().timeIntervalSince1970)")
timer = DispatchSource.makeTimerSource(flags: .strict, queue: .global())
timer?.schedule(deadline: .now() + 2, repeating: .never)
timer?.setEventHandler {
print("time:\(Date.init().timeIntervalSince1970) -- \(Thread.current)")
}
//timer?.setEventHandler(handler: DispatchWorkItem.init(qos: .userInteractive, flags: [], block: {
// print("time:\(Date.init().timeIntervalSince1970) -- \(Thread.current)")
// }))
for i in 0 ..< 1000000{
print(i)
}
上面这样一个例子,如果全局队列,那么可以在start的2秒后输出time;如果改成.main,会在七八秒甚至更久之后才能输出,因为主队列不会开启新线程,只能等当前的函数走完,才执行timer的handler,即便创建workitem自定义优先级也不行;
因此makeTimerSource时使用主队列需要谨慎,如果后面有耗时操作,那延时就不准了,或者说耗时操作也不要放在主线程.
在handler里,要么使用weak,要么记得在合适的时候调用cancel,否则会引起循环强引用.
2. DispatchSourceMemoryPressure
class func makeMemoryPressureSource(eventMask: DispatchSource.MemoryPressureEvent, queue: DispatchQueue? = nil) -> DispatchSourceMemoryPressure
和timer一样,也是一个协议,通过初始化方法获取对象
MemoryPressureEvent是内存变换消息,有四种选项:
.all是所有状态都监听
.normal是监听回复正常的消息
.warning是监听内存警告的消息
.critical是监听内存进入紧急状态的消息
和DispatchSource.TimerFlags一样,它也定义了一大堆的比较方法.
let m = DispatchSource.makeMemoryPressureSource(eventMask: .all, queue: .global())
m.setEventHandler {
print("内存状态变换")
}
m.activate()
只是写这段代码不会有log出现,因为内存没有什么波澜.
3. DispatchSourceProtocol
这个协议规定了Dispatch Source的一些行为.前言说的那几种source都遵循这个协议.
activate()
source初始化时是不活跃的,需要调用activate()来进入工作状态;
也可以使用resume()来开始
resume()
恢复工作状态,和下面的方法一一对应,存在计数关系;
当处在工作状态时,再调用会crash;
suspend()
暂停source的监听,它和resume()一一对应的,会有一个计数关系,调用几次suspend就得调用resume才能恢复;
当处在暂停状态时,再调用会crash;
repeating设置为.never的timer,只会执行一次,使用suspend()和resume()也没有意义
cancel()
取消source;
另外activate(),activate(),suspend()状态的source都不能直接释放,会crash,需要先cancel()
func setEventHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: Self.DispatchSourceHandler?)
func setEventHandler(handler: DispatchWorkItem)
设置event就是添加workItem的两种方式
func setCancelHandler(handler: DispatchWorkItem)
func setCancelHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: Self.DispatchSourceHandler?)
设置调用cancel()的回调任务
func setRegistrationHandler(handler: DispatchWorkItem)
func setRegistrationHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: Self.DispatchSourceHandler?)
设置完成初始化source的回调任务,对于timer来说,会走在第一次执行之前
二:Dispatch Semaphore
通过wait和signal来进行线程间的数据同步.
let sem = DispatchSemaphore.init(value: 0)
for i in 0 ..< 100{
DispatchQueue.global().async {
print("\(i) + \(Thread.current)")
let count = sem.signal()
print(count)
}
sem.wait()
}
调用signal()信号量为+1,并且返回当前的信号量;
调用wait()信号量-1,大概信号量为0时,阻塞当前线程,等待信号量>0的时刻;
image.png
wait()可以设置时长,如sem.wait(timeout: .now() + 1),和workItem的效果一样,设置一个最迟的等待时间,是如果时间到了任务还没完成,也不再等待,直接返回.
如果时间到之前任务已经完成,则也是立即返回,
返回结果是枚举值DispatchTimeoutResult,有两个值.success和.timeout对应两种情况.
网友评论