美文网首页
iOS Swift GCD DispatchPredicate

iOS Swift GCD DispatchPredicate

作者: FicowShen | 来源:发表于2019-01-11 18:47 被阅读25次



    在说这两个类型之前,先介绍一个函数:

    func dispatchPrecondition(condition: @autoclosure () -> DispatchPredicate)
    

    这个函数可以用来确保当前多线程操作的可靠性,比如:

    aDispatchQueue.async {
        dispatchPrecondition(condition: .onQueue(.main))
        self.view.alpha = 1
    }
    

    以上代码可以确保在aDispatchQueue中操作UI属性前,确保此线程是主线程。
    如果当前线程不是主线程,后续操作不会发生。其效果与以下代码的效果相似:

    guard Thread.isMainThread else { return }
    



    DispatchPredicate

    public enum DispatchPredicate {
        case onQueue(DispatchQueue)
        case onQueueAsBarrier(DispatchQueue)
        case notOnQueue(DispatchQueue)
    }
    

    onQueue, notOnQueue是很容易理解的,确保当前队列是不是某队列。



    关于onQueueAsBarrier,请看这个示例:

    let concurrentQueue = DispatchQueue(label: "queue", attributes: .concurrent)
    var numbers = [0, 1]
    concurrentQueue.async { print(numbers.first!) }
    concurrentQueue.async(flags: .barrier) {
        dispatchPrecondition(condition: .onQueueAsBarrier(concurrentQueue))
        Thread.sleep(forTimeInterval: 0.25)
        numbers = [2]
    }
    concurrentQueue.async { print(numbers.last!) }
    

    以上代码会输出:

    0
    2
    

    async(flags: .barrier) 可以使操作具有独占性,在并发队列中,可以防止发生数据竞争。

    在这里,dispatchPrecondition(condition: .onQueueAsBarrier(q)) 可以确保当前block中的操作都是在concurrentQueue中具有独占性的(barrier操作),也就是在修改numbers数组时,不会有其他线程同时在对numbers进行操作。
    在处理读写并发操作时,如果你的读写逻辑代码比较分散,这个条件就可以很高效地确保分散在各处的写操作具有独占性,防止读写操作同时进行导致意外(返回结果不符合预期、程序崩溃等)发生!



    DispatchWorkItem

    public class DispatchWorkItem {
        public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> Void)
        public func perform()
        public func wait()
        public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
        public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
        public func notify(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, queue: DispatchQueue, execute: @escaping @convention(block) () -> Void)
        public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
        public func cancel()
        public var isCancelled: Bool { get }
    }
    

    其核心就是封装一个可以执行的闭包。

    可以通过perform()方法,直接执行内部的闭包。
    也可以将DispatchWorkItem对象添加到DispatchQueue当中,然后由DispatchQueue去调度执行DispatchWorkItem内部的闭包。

    let workItem = DispatchWorkItem.init { print("working") }
    
    DispatchQueue.global().async { workItem.perform() }
    DispatchQueue.global().async(execute: workItem)
    



    DispatchWorkItemFlags

    上面的示例代码中其实已经用到了DispatchWorkItemFlags.barrier

    concurrentQueue.async(flags: .barrier)
    

    如果你去查看关于DispatchWorkItemFlags的官网文档,你会发现官网并没有太详细的解释,尤其是对以下几个参数没有具体的介绍。

    static let assignCurrentContext: DispatchWorkItemFlags
    static let barrier: DispatchWorkItemFlags
    static let detached: DispatchWorkItemFlags
    static let enforceQoS: DispatchWorkItemFlags
    static let inheritQoS: DispatchWorkItemFlags
    static let noQoS: DispatchWorkItemFlags
    

    -_-+ 其实,你应该查看的是关于dispatch_block_flags_t的介绍。



    所以根据以上文档,可以这样理解以上几个参数:

    • assignCurrentContext
      表明DispatchWorkItem在被创建时,应该被指定执行上下文参数。这些参数包括:QoS class, os_activity_t 和进程间通信请求参数。
      如果DispatchWorkItem被直接调用,DispatchWorkItem在调用的线程中将采用这些参数。
      如果DispatchWorkItem被提交到队列中,这些参数会被提交时的执行上下文中的参数替代。
      如果QoS类为DISPATCH_BLOCK_NO_QOS_CLASS或dispatch_block_create_with_qos_class生成的值,那么这个值会取代当前的值。
    • barrier
      这个是比较常用的参数。
      如果DispatchWorkItem被提交到.concurrent并发队列,那么这个DispatchWorkItem中的操作会具有独占性(防止此DispatchWorkItem中的block内的操作与其他操作同时执行)。
      如果直接执行这个DispatchWorkItem,没有任何效果。



    以下代码仅供参考:

    let q = DispatchQueue(label: "ficow-test-queue", attributes: .concurrent)
    
    q.async {
        print("a")
    }
    q.async(flags: .barrier) {
        Thread.sleep(forTimeInterval: 0.25)
        print("barrier")
    }
    q.async {
        print("b")
    }
    

    以上代码会输出:

    a
    barrier
    b
    

    如果没有.barrier这个属性,输出内容将是:

    a
    b
    barrier
    
    • detached
      表明DispatchWorkItem会无视当前执行上下文的参数(QoS class, os_activity_t 和进程间通信请求参数)。
      如果直接执行DispatchWorkItem,在复制这些属性给这个block前,block在执行期间会移除在调用线程中的这些属性。
      如果DispatchWorkItem被添加到队列中,block在执行时会采用队列的属性,或者赋值给block的属性。
    • enforceQoS
      表明DispatchWorkItem会采用当前的QoS class,而不是队列的。
    • inheritQoS
      表明DispatchWorkItem会采用队列的QoS class,而不是当前的。
    • noQoS
      不指定QoS,由调用线程或队列来指定。





    参考文章:
    DispatchWorkItemFlags
    dispatch_block_flags_t
    Swift多线程编程总结

    相关文章

      网友评论

          本文标题:iOS Swift GCD DispatchPredicate

          本文链接:https://www.haomeiwen.com/subject/sblwlqtx.html