Swift4中使用GCD----分组和信号量

DispatchWorkItem:

DispatchWorkItem是一个任务的封装对象,可以复用,类比Operation,它有一些自己的想法,定义如下:

    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 }

它可以自己执行、等待、通知别的队列、取消等,它是GCD执行任务的最终对象,用Block传入的任务最后也是包装成DispatchWorkItem。一般情况下,不会单独用它来执行任务的,需要配合DispatchQueue和DispatchGroup。

DispatchGroup:

假如现在有一个任务A,需要依赖三个任务A1、A2、A3,此时我们就需要DispatchGroup了,它可以把A1、A2、A3装到组里,等3个任务全部完成以后,通知任务A。代码类似这样:

        let group = DispatchGroup.init()
        //任务A1
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            //沉睡两秒,模拟耗时任务
            Thread.sleep(forTimeInterval: 2)
            print("完成任务A1\(Thread.current)")
        }))
        
        //任务A2
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            //沉睡两秒,模拟耗时任务
            Thread.sleep(forTimeInterval: 2)
            print("完成任务A2\(Thread.current)")
        }))
        
        //任务A3
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            //沉睡两秒,模拟耗时任务
            Thread.sleep(forTimeInterval: 3)
            print("完成任务A3\(Thread.current)")
        }))
        
        //通知任务A,三个任务已经全部完成
        group.notify(queue: DispatchQueue.main) {
            print("全部完成\(Thread.current)")
        }
//        group.wait()
//
//        DispatchQueue.main.async(group: group, execute: DispatchWorkItem.init(block: {
//            print("全部完成\(Thread.current)")
//        }))
        
        //任务B
        print("异步任务不会阻塞线程")

打印结果如下:

由于是异步任务,不会阻塞线程,任务B首先完成,然后加入到group的异步任务分别创建线程完成任务,全部完成以后,再来通知任务A。

DispatchGroup还有个方法 wait(),会阻塞当前线程,直到任务完成,如果使用wait()方法的话,打印结果如下:
从结果来看,group等待任务A1、A2、A3首先完成,然后执行异步任务A,异步任务会立即返回,所以先执行了任务B。

DispatchSemaphore:

现在来增加点有趣的内容,加入任务A1、A2、A3里再开启异步任务,比如网络请求,代码如下:

        let group = DispatchGroup.init()
        //任务A1
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            
            //开启异步任务
            DispatchQueue.global().async {
                //模拟网络请求两秒后返回
                Thread.sleep(forTimeInterval: 2)
                print("完成网络请求A1\(Thread.current)")
            }
            print("完成任务A1\(Thread.current)")
        }))
        
        //任务A2
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            //开启异步任务
            DispatchQueue.global().async {
                //模拟网络请求两秒后返回
                Thread.sleep(forTimeInterval: 2)
                print("完成网络请求A2\(Thread.current)")
            }
            print("完成任务A2\(Thread.current)")
        }))
        
        //任务A3
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            //开启异步任务
            DispatchQueue.global().async {
                //模拟网络请求两秒后返回
                Thread.sleep(forTimeInterval: 2)
                print("完成网络请求A3\(Thread.current)")
            }
            print("完成任务A3\(Thread.current)")
        }))
        
        //通知任务A,三个任务已经全部完成
        group.notify(queue: DispatchQueue.main) {
            print("全部完成\(Thread.current)")
        }
//        group.wait()
//
//        DispatchQueue.main.async(group: group, execute: DispatchWorkItem.init(block: {
//            print("全部完成\(Thread.current)")
//        }))
        
        //任务B
        print("异步任务不会阻塞线程")

此时的执行顺序会怎样呢?

这个时候,任务A1、A2、A3都提前返回了,网络请求什么时候返回也不一定,如果我们需要依靠网络请求返回的结果,这个时候该怎么办呢?这个时候DispatchSemaphore就该闪亮登场了。DispatchSemaphore的定义如下:

open class DispatchSemaphore : DispatchObject {
}

/// dispatch_semaphore
extension DispatchSemaphore {

    public func signal() -> Int

    public func wait()

    public func wait(timeout: DispatchTime) -> DispatchTimeoutResult

    public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
}
extension DispatchSemaphore {

    
    /*!
     * @function dispatch_semaphore_create
     *
     * @abstract
     * Creates new counting semaphore with an initial value.
     *
     * @discussion
     * Passing zero for the value is useful for when two threads need to reconcile
     * the completion of a particular event. Passing a value greater than zero is
     * useful for managing a finite pool of resources, where the pool size is equal
     * to the value.
     *
     * @param value
     * The starting value for the semaphore. Passing a value less than zero will
     * cause NULL to be returned.
     *
     * @result
     * The newly created semaphore, or NULL on failure.
     */
    @available(iOS 4.0, *)
    public /*not inherited*/ init(value: Int)
}

DispatchSemaphore的使用很简单:
初始化一个开始的量,wait()方法使信号量-1,当信号量变成0,阻塞当前线程,等待信号量大于0,恢复线程。signal()方法使信号量+1,配合wait()可以控制线程并发量。
来看看怎么用DispatchSemaphore来让网络任务同步:

        let group = DispatchGroup.init()
        //任务A1
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            
            //创建信号量为0
            let semaphore = DispatchSemaphore.init(value: 0)
            //开启异步任务
            DispatchQueue.global().async {
                //模拟网络请求两秒后返回
                Thread.sleep(forTimeInterval: 2)
                print("完成网络请求A1\(Thread.current)")
                semaphore.signal()
            }
            //调用wait()方法,此时信号量为0,会阻塞当前线程,任务A1不会返回,所以就不会通知任务A
            semaphore.wait()
            print("完成任务A1\(Thread.current)")
        }))
        
        //任务A2
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            
            //创建信号量为0
            let semaphore = DispatchSemaphore.init(value: 0)
            //开启异步任务
            DispatchQueue.global().async {
                //模拟网络请求两秒后返回
                Thread.sleep(forTimeInterval: 2)
                print("完成网络请求A2\(Thread.current)")
                semaphore.signal()
            }
            //调用wait()方法,此时信号量为0,会阻塞当前线程,任务A2不会返回,所以就不会通知任务A
            semaphore.wait()
            print("完成任务A2\(Thread.current)")
        }))
        
        //任务A3
        DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
            
            //创建信号量为0
            let semaphore = DispatchSemaphore.init(value: 0)
            //开启异步任务
            DispatchQueue.global().async {
                //模拟网络请求两秒后返回
                Thread.sleep(forTimeInterval: 2)
                print("完成网络请求A3\(Thread.current)")
                semaphore.signal()
            }
            //调用wait()方法,此时信号量为0,会阻塞当前线程,任务A3不会返回,所以就不会通知任务A
            semaphore.wait()
            print("完成任务A3\(Thread.current)")
        }))
        
        //通知任务A,三个任务已经全部完成
        group.notify(queue: DispatchQueue.main) {
            print("全部完成\(Thread.current)")
        }
//        group.wait()
//
//        DispatchQueue.main.async(group: group, execute: DispatchWorkItem.init(block: {
//            print("全部完成\(Thread.current)")
//        }))
        
        //任务B
        print("异步任务不会阻塞线程")

来看看打印结果:

网络请求要先返回,才会完成任务A1、A2、A3,然后才会通知任务A,同样的,任务B最先完成。

总结:

通过DispatchGroup和DispatchSemaphore可以给任务分组,把异步任务变成同步任务,组合使用效果更佳。

你可能感兴趣的:(Swift4中使用GCD----分组和信号量)