iOS GCD学习总结(二)

1.GCD 线程间的通信

在 iOS 开发过程中,当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。

    func communication(){
        DispatchQueue.global().async {          //异步添加任务 例如:文件上传、下载、接口请求
            sleep(2)                            //模拟耗时操作
            DispatchQueue.main.async {
                self.tableView.reloadData()     //更新ui
            }
        }
    }

如果想了解为啥一定要在主线程更新ui的话,https://juejin.im/post/5c406d97e51d4552475fe178https://www.cnblogs.com/8335IT/p/10373723.html

1.GCD 其他方法

栅栏方法:dispatch_barrier_async

一个dispatch barrier 允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block,等待所有在barrier之前提交的blocks执行结束。 这时,barrier block自己开始执行。 之后, 队列继续正常的执行操作。这里指定的并发队列应该是自己通过dispatch_queue_create函数创建的。如果你传的是一个串行队列或者全局并发队列,这个函数等同于dispatch_async函数。
所有在barrier block之后提交的blocks会等到barrier block结束之后才执行

    /**
    *  栅栏方法 dispatch_barrier_async 
    *  基本操作
    */
    func barrier(){
        let queue = DispatchQueue.init(label: "barrier", attributes: .concurrent)
        queue.async {//追加任务1
            sleep(2)
            print("1---\(Thread.current)")
        }
        queue.async {//追加任务2
            sleep(2)
            print("2---\(Thread.current)")
        }
        queue.async(flags: .barrier) {//追加任务barrier
            sleep(1)
            print("barrier---\(Thread.current)")
        }
        queue.async {//追加任务3
            print("3---\(Thread.current)")
        }
    }
输出:
2---<NSThread: 0x282250ac0>{number = 5, name = (null)}
1---<NSThread: 0x282276bc0>{number = 6, name = (null)}
barrier---<NSThread: 0x282276bc0>{number = 6, name = (null)}
3---<NSThread: 0x282276bc0>{number = 6, name = (null)}

从以上可以看出在执行完栅栏前面的操作之后,才执行栅栏操作,最后再执行栅栏后边的操作

那如果dispatch_barrier_sync用同步会有什么效果了呢?

    /**
    * 栅栏方法 dispatch_barrier_sync
    */
    func syncbarrier(){
        let queue = DispatchQueue.init(label: "syncbarrier", attributes: .concurrent)
        queue.async {
            sleep(2)
            print("1---\(Thread.current)")
        }
        queue.sync(flags: .barrier) {
            sleep(1)
            print("barrier---\(Thread.current)")
        }
        
        print("我在主线程上---\(Thread.current)")
        
        queue.async {
            print("3---\(Thread.current)")
        }
    }
输出:
1---<NSThread: 0x280860b80>{number = 5, name = (null)}
barrier---<NSThread: 0x280802e40>{number = 1, name = main}
我在主线程上---<NSThread: 0x280802e40>{number = 1, name = main}
3---<NSThread: 0x280860b80>{number = 5, name = (null)}

以上输出可以看出,dispatch_barrier_sync如果传入自己创建的并行队列时,阻塞当前队列的同时也会阻塞当前线程,而dispatch_barrier_async不会阻塞当前线程。更多dispatch_barrier_sync用法请转:dispatch_barrier_sync实现多读单写

延时执行方法:dispatch_after

dispatch_after 方法并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after 方法是很有效的

    /**
    * 延时执行方法 dispatch_after
    */
    func after(){
        print("1---\(Thread.current)")
        print("begin")
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            print("after---\(Thread.current)")//延迟2秒输出
        }
        print("end")
    }
输出:
1---<NSThread: 0x174078780>{number = 1, name = main}
begin
end
after---<NSThread: 0x174078780>{number = 1, name = main}
一次性代码(只执行一次):dispatch_once

我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了 GCD 的 dispatch_once 方法。使用 dispatch_once 方法能保证某段代码在程序运行过程中只被执行 1 次,并且即使在多线程的环境下,dispatch_once 也可以保证线程安全。因 swift4.0已废弃dispatch_once,所以需要手动实现

//MARK:扩展实现一次性代码
public extension DispatchQueue{
     private static var _onceTracker = [String]()
    
    class func once(token:String, block:()->()){
        objc_sync_enter(self)
        defer {
            objc_sync_exit(self)
        }
        guard !_onceTracker.contains(token) else {return}
        _onceTracker.append(token)
        block()
    }
}

  //调用
  DispatchQueue.once(token: "uniquely_identifies") {
       print("Do This Once!")
  }

defer:延迟调用,调用时机是在离开作用域之后,其他语句之前 。
objc_sync_enter、objc_sync_exit:保证线程安全

快速迭代方法:dispatch_apply

该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束,好处是可以重复执行某项操作并复用我们的Block了,swift 已废弃该方法。

    /**
    * 快速迭代方法:dispatch_apply
    */
    func apply(){
        //如果把队列改成串行就是按顺序执行
        let queue = DispatchQueue.init(label: "apply", attributes: .concurrent)
        queue.async {
            DispatchQueue.concurrentPerform(iterations: 5) { (index) in
                print("重复操作:\(index)---\(Thread.current)")
            }
            print("end")
        }
    }
    //输出:
    重复操作:1---<NSThread: 0x17007b280>{number = 4, name = (null)}
    重复操作:2---<NSThread: 0x17007b280>{number = 4, name = (null)}
    重复操作:3---<NSThread: 0x17007b280>{number = 4, name = (null)}
    重复操作:4---<NSThread: 0x17007b280>{number = 4, name = (null)}
    重复操作:0---<NSThread: 0x17407fc40>{number = 3, name = (null)}
    end
    //例:
    func apply(){
        let dictArray:[[String:Any]] = []
        let queue = DispatchQueue.init(label: "apply", attributes: .concurrent)
        queue.async {
            DispatchQueue.concurrentPerform(iterations: dictArray.count) { (index) in
                //字典转模型
            }
            DispatchQueue.main.async {
                //主线程更新
            }
        }
    }
队列组:dispatch_group

有时候我们会有个这样的需求,要等到两个或更多的耗时任务都完成后再做其他操作,这时候我们可以想到队列组

dispatch_group_notify:

    /**
    * dispatch_group_notify
    */
    func group(){
        let group = DispatchGroup.init()
        let queue = DispatchQueue.init(label: "group", attributes: .concurrent)
        queue.async(group: group) {
            sleep(2)//耗时任务
            print("1---\(Thread.current)")
        }
        queue.async(group: group) {
            sleep(5)//耗时任务
            print("2---\(Thread.current)")
        }
        group.notify(queue: DispatchQueue.main) {
            print("main---\(Thread.current)")
        }
    }
    //输出:
    1---<NSThread: 0x282cbf7c0>{number = 1, name = main}
    2---<NSThread: 0x282cbf7c0>{number = 1, name = main}
    main---<NSThread: 0x282cbf7c0>{number = 1, name = main}

dispatch_group_wait:

    /**
    * dispatch_group_wait
    */
    func groupWait(){
        let group = DispatchGroup.init()
        let queue = DispatchQueue.init(label: "group", attributes: .concurrent)
        queue.async(group: group) {
            sleep(2)//耗时任务
            print("1---\(Thread.current)")
        }
        queue.async(group: group) {
            sleep(5)//耗时任务
            print("2---\(Thread.current)")
        }
        // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
        group.wait()
        print("end")
    }
输出:
1---<NSThread: 0x283aa9940>{number = 6, name = (null)}
2---<NSThread: 0x283abc200>{number = 5, name = (null)}
end

dispatch_group_enter、dispatch_group_leave:

    /**
    * dispatch_group_enter、dispatch_group_leave
    */
    func groupEnterAndLeave(){
        let group = DispatchGroup.init()
        let queue = DispatchQueue.init(label: "groupEnterAndLeave", attributes: .concurrent)
        group.enter()
        queue.async(group: group) {
            sleep(2)//耗时任务
            print("1---\(Thread.current)")
            group.leave()
        }
        
        group.enter()
        queue.async(group: group) {
            sleep(5)//耗时任务
            print("2---\(Thread.current)")
            group.leave()
        }
        
        group.notify(queue: DispatchQueue.main) {
            print("end")
        }
    }
输出:
1---<NSThread: 0x281efecc0>{number = 5, name = (null)}
2---<NSThread: 0x281ef77c0>{number = 6, name = (null)}
end

简单说一下原理,group是基于信号量现实,group的本质是一个初始值为LONG_MAX的信号量,通过信号量来实现各个任务的管理

iOS GCD学习总结(一)
iOS 线程同步方案学习总结
信号量semaphore学习总结
iOS dispatch_barrier_sync实现多读单写
NSOperation和NSOperationQueue学习总结

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

推荐阅读更多精彩内容