美文网首页
iOS GCD学习总结(二)

iOS GCD学习总结(二)

作者: 西门吹水Jacky | 来源:发表于2020-06-16 17:52 被阅读0次

    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学习总结

    相关文章

      网友评论

          本文标题:iOS GCD学习总结(二)

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