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可以给任务分组,把异步任务变成同步任务,组合使用效果更佳😜。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355

推荐阅读更多精彩内容

  • 我很好奇自己会出现怎样的变化,究竟量变引起质变的临界点在那里?就像讲白话一样,似乎到现在自己也讲得不好,但自己如今...
    脏鞋子阅读 258评论 0 0
  • Today is Saturday. Amelia leaped out of bed and dressed i...
    Mr_Oldman阅读 170评论 0 0
  • 今天看了一篇文章很有感触,“在上了床也没有结果的年代,怀念牵了手就是一生的爱情”,没出息的我,看哭了。 是啊,多么...
    安茶阅读 1,440评论 24 16