swift GCD 的一些高级用法

信号量

之前遇到一个问题,一个请求需要在另一个请求获得的参数。这个时候最开始的办法是把第二个请求写在第一个请求的回调里,但是这样的话,两个请求就很紧密的耦合在一起了。这个时候可以使用信号量来使他们分离开来。
先看下相关的3个方法:

dispatch_semaphore_t dispatch_semaphore_create(long value):方法接收一个long类型的参数, 返回一个dispatch_semaphore_t类型的信号量,值为传入的参数
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout):接收一个信号和时间值,若信号的信号量为0,则会阻塞当前线程,直到信号量大于0或者经过输入的时间值;若信号量大于0,则会使信号量减1并返回,程序继续住下执行
long dispatch_semaphore_signal(dispatch_semaphore_t dsema):使信号量加1并返回

下面看几种使用方法
保持线程同步

        let semaphore = DispatchSemaphore.init(value: 0)
        var i = 10
        DispatchQueue.global().async {
            i = 100
            
            semaphore.signal()
        }
        semaphore.wait()
        print("i = \(i)")

输出i = 100
如果注掉semaphore.wait()这一行,则 i = 10
注释: 由于是将block异步添加到一个并行队列里面,所以程序在主线程跃过block直接到semaphore.wait()这一行,因为semaphore信号量为0,所以当前线程会一直阻塞,直到block在子线程执行到semaphore.signal(),使信号量+1,此时semaphore信号量为1了,所以程序继续往下执行。这就保证了线程间同步了。

为线程加锁(同时可以控制最大并发数量 , value 的值等于几就是最多几个并发)

        let semaphore = DispatchSemaphore.init(value: 1)
        for i in 0..<100 {
            DispatchQueue.global().async {
                semaphore.wait()
                print("i = \(i)")
                semaphore.signal()
            }
            
        }

注释:当线程1执行到semaphore.wait()这一行时,semaphore的信号量为1,所以使信号量-1变为0,并且线程1继续往下执行;如果当在线程1print这一行代码还没执行完的时候,又有线程2来访问,执行semaphore.wait()时由于此时信号量为0(.wait()方法默认时间是 OC 的DISPATCH_TIME_FOREVER),所以会一直阻塞线程2(此时线程2处于等待状态),直到线程1执行完print并执行完semaphore.signal()使信号量为1后,线程2才能解除阻塞继续住下执行。以上可以保证同时只有一个线程执行print这一行代码。

栅栏函数(barrier)

等待异步执行多个任务后, 再执行下一个任务,一般使用barrier函数

        //创建串行队列
//        let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .init(rawValue: 0), autoreleaseFrequency: .workItem, target: nil)
        //创建并行队列
        let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
        
        queue.async {//任务一
            for _ in 0...3 {
                print("......")
            }
        }
        queue.async {//任务二
            for _ in 0...3 {
                print("++++++");
            }
        }
        
        queue.async(group: nil, qos: .default, flags: .barrier) {
            print("group")
        }
        
        queue.async {
            print("finish")
        }
最后打印
......
++++++
++++++
++++++
++++++
......
......
......
group
finish

注释:使用barrier函数可以做到先让前面的任务执行完毕,再执行之后的任务,会阻塞当前的线程

延时任务

        let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
        queue.async {//任务一
            for _ in 0...3 {
                print("......")
            }
        }
        print("0")
        queue.asyncAfter(deadline: DispatchTime.now() + 10, execute: {
            print("延时提交的任务")
        })
        
        queue.async {//任务二
            for _ in 0...3 {
                print("++++++");
            }
        }

打印:

注释10s后提交。并且不会阻碍当前线程

组的用法(Group)

notify(依赖任务)

        let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
        
        let group = DispatchGroup()
        queue.async(group: group, qos: .default, flags: [], execute: {
            for _ in 0...10 {
                
                print("耗时任务1")
            }
        })
        queue.async(group: group, qos: .default, flags: [], execute: {
            for _ in 0...10 {
                
                print("耗时任务2")
            }
        })
        //执行完上面的两个耗时操作, 回到myQueue队列中执行下一步的任务
        group.notify(queue: queue) {
            print("回到该队列中执行")
        }
        queue.async {
            print("完成")
        }

打印:
耗时任务2
完成
耗时任务1
耗时任务2
耗时任务2
耗时任务2
耗时任务2
耗时任务1
耗时任务2
耗时任务1
耗时任务1
耗时任务1
耗时任务1
回到该队列中执行

注释:使用group+notify的话,也会等待之前的任务先执行完,和barrier的区别是不会阻碍当前的线程

wait(等待任务)

        let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
        
        let group = DispatchGroup()
        queue.async(group: group, qos: .default, flags: [], execute: {
            for _ in 0...5 {
                
                print("耗时任务1")
            }
        })
        queue.async(group: group, qos: .default, flags: [], execute: {
            for _ in 0...5 {
                
                print("耗时任务2")
            }
        })
        //等待上面任务执行,会阻塞当前线程,超时就执行下面的,上面的继续执行。可以无限等待 .distantFuture
        let result = group.wait(timeout: .now() + 2.0)
        switch result {
        case .success:
            print("不超时, 上面的两个任务都执行完")
        case .timedOut:
            print("超时了, 上面的任务还没执行完执行这了")
        }
        
        print("接下来的操作")

打印:
耗时任务1
耗时任务2
耗时任务1
耗时任务2
耗时任务1
耗时任务2
耗时任务1
耗时任务2
耗时任务1
耗时任务2
耗时任务1
耗时任务2
不超时, 上面的两个任务都执行完
接下来的操作

注释:使用wait+group的话,如果设置timeout = .distantFuture的话,那么就和barrier函数一样了,会等待之前的完成,否则就是等待之前的完成或者等待设置的时间到了,就会执行接下来的任务了,会阻塞当前现场。

参考:
iOS GCD之dispatch_semaphore学习 来自萌小菜

Swift 3.0 GCD的常用方法 来自床前明月_光

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