重学GCD之DispatchWorkItem

1,770 阅读2分钟

重学GCD之DispatchWorkItem

DispatchWorkItem是iOS8时候推出的一套API, 增加了一些对任务控制的API. 与Operation对象或者BlockOperation比较像, 主要可以主动cancel, 并且增加了notify功能

具体的新增功能, 直接在注释里面看吧

@available(macOS 10.10, iOS 8.0, *)
public class DispatchWorkItem {
    // 初始化方法 => 最关键的是 最后的 block, 实际就是 我们要执行的 TaskItem
    public init(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], block: @escaping @convention(block) () -> Void)
​
    /// sync 同步执行
    public func perform()
​
    /// 内置信号量 - 同步等待任务执行完成
    public func wait()
​
    /// 内置信号量 - 同步等待指定时间, 等待任务完成
    public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
    public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult/// 内置信号量, WorkItem 执行完成或者被调度, 信号量触发通知, 可以指定 barrier
    public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute: @escaping @convention(block) () -> Void)
    public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
​
    /// 可以取消, 异步将 WorkItem内部的state设置层 isCancelled. 被调度立刻返回(注意, 也会触发notify)
    public func cancel()
​
    /// 判断WorkItem是否被取消的状态
    public var isCancelled: Bool { get }
}

可以看出, GCD 中的DispatchWorkItem就如同OperationQueue中的BlockOperation类似的控制粒度!!! 以后在GCD中使用可以取消 workItem !!!

let workQueue = DispatchQueue(label: "com.webank.serialQueue")
var item: DispatchWorkItemfunc testDispatchItems() {
  let myObj = MyObj()
​
  item = DispatchWorkItem { [weak self] in
                           print("item is start...")
                           for _ in 0 ... 5 {
                             if self?.item.isCancelled ?? true {
                               print("item isCancelled")
                               break
                             }
                             print(myObj.self)
                             self?.heavyWork()
                           }
​
                           print("item is end...")
                          }
​
  item.notify(queue: DispatchQueue.main, execute: {
    print("item?.notify(queue: DispatchQueue.main, exe!!!!")
  })
​
  workQueue.async {
    print("preview task start...")
    sleep(5)
    print("preview task end...")
  }
​
  workQueue.async(execute: item)
​
  // 延迟2s取消
  DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
    self.item.cancel()
  }
}
​
func heavyWork() {
  sleep(1)
  print("heavyWork")
}

基于 DispatchWorkItem 的防抖和限流

// 数据节流器
class Throttle {
    private let label: String
    private let interval: Int
    private let queue: DispatchQueue
    private var workItem: DispatchWorkItem?
    private var lock: DispatchSemaphoreWrapper
    private var lastTime: Date = Date()
    
    /// interval: 单位毫秒
    init(label: String = "Throttler", interval: Int = 500) {
        self.label = label
        self.interval = interval
        self.queue = DispatchQueue(label: label)
        self.lock = DispatchSemaphoreWrapper(value: 1)
    }
    
    func call(_ callback: @escaping (() -> ())) {
        self.lock.sync {
            self.workItem?.cancel()
            self.workItem = DispatchWorkItem { [weak self] in
                self?.lastTime = Date()
                callback()
            }
            if let workItem = self.workItem {
                let new = Date()
                let delay = new.timeIntervalSince1970 - lastTime.timeIntervalSince1970 > Double(self.interval)/1000 ? DispatchTimeInterval.milliseconds(0) : DispatchTimeInterval.milliseconds(self.interval)
                self.queue.asyncAfter(deadline: .now() + delay, execute: workItem)
                
            }
        }
    }
}
​
// 网络防抖
class Debouncer {
    private let label: String
    private let interval: Int
    private let queue: DispatchQueue
    private var workItem: DispatchWorkItem?
    private var lock: DispatchSemaphoreWrapper
    
    /// interval: 单位毫秒
    init(label: String = "Debouncer", interval: Int = 500) {
        self.label = label
        self.interval = interval
        self.queue = DispatchQueue(label: label)
        self.lock = DispatchSemaphoreWrapper(value: 1)
    }
    
    func call(_ callback: @escaping (() -> ())) {
        self.lock.sync {
            self.workItem?.cancel()
            self.workItem = DispatchWorkItem {
                callback()
            }
            
            if let workItem = self.workItem {
                self.queue.asyncAfter(deadline: .now() + DispatchTimeInterval.milliseconds(interval), execute: workItem)
            }
        }
    }
}
​
struct DispatchSemaphoreWrapper {
    private var lock: DispatchSemaphore
    
    init(value: Int) {
        self.lock = DispatchSemaphore(value: value)
    }
    
    func sync(excute: () -> ()) {
        _ = lock.wait(timeout: DispatchTime.distantFuture)
        lock.signal()
        excute()
    }
}

具体的 测试方法如下:

func testThrottle() {
  for _ in 0..<10 {
    sleep(1)
    throttle.call {
      let date = Date()
      print("XQ:(date.timeIntervalSince1970)")
    }
  }
}
​
func testDebouncer() {
  for i in 0..<100 {
    let delay = i % 2 == 0 ? 1 : 3
    sleep(UInt32(delay))
    debouncer.call {
      let date = Date()
      print("XQ:(date.timeIntervalSince1970)")
    }
  }
}