美文网首页
iOS 多线程 GCD (Swift 3)

iOS 多线程 GCD (Swift 3)

作者: 24小时营业 | 来源:发表于2017-09-01 00:42 被阅读0次

    0. 前言

    GCD(Grand Central Dispatch)是 Apple 开发的一种多核编程的方法,主要的作用是优化应用程序对多核处理器的运用,是在线程池模式的基础上执行的并行任务。GCD中有两个重要的基本概念——任务队列


    1. 任务和队列

    任务

    就是执行的操作,也就是在线程中执行的代码,封装在 Swift 的闭包(Closure)或者 OC 的块(block)里。根据能否具备开启新线程可以分成 同步执行异步执行

    • 同步执行(sync) 只能在当前线程中执行任务,不能开新线程,当前的代码段没有执行完就不能够执行下一部分,会阻塞当前线程。
    • 异步执行(async) 可以在新线程中执行任务,能够开启新线程,不用等待当前的代码段执行完就可以往下执行后续代码,不会阻塞当前线程。

    队列

    就是任务队列,是一种特殊的线性表,满足 FIFO 的原则。在 GCD 里分为 串行队列并发队列

    • 串行队列(Serial Dispatch Queue) 一个任务完成后接着执行下一个任务。
    • 并发队列(Concurrent Dispatch Queue) 自动开启多个线程进行多个任务并发执行,而且并发只有在异步执行(dispatch_async)情况下才有效。

    2. 创建队列

    系统默认队列——主队列(串行)

    //主队列
    let mainQueue = DispatchQueue.main
    

    系统默认队列——全局队列(并发)

    //全局队列
    let globalQueue = DispatchQueue.global()
    

    全局队列有 4 个执行的优先级,从高到低分别是:
    .userInitiated.default.utility.background
    所以当需要创建指定某个优先级的全局队列时:

    let defaultGlobalQueue = DispatchQueue.global(qos: .default)
    

    自定义创建队列

    //创建串行队列
    let serialQueue = DispatchQueue(label: "serialQueue_1")
    //创建优先级为default的串行队列
    let serialQueue = DispatchQueue(label: "serialQueue_2", qos: .default)
    
    //创建优先级为background的并发队列
    let concurrentQueue = DispatchQueue(label: "concurrentQueue", qos: .background, attributes: .concurrent)
    

    队列执行

    队列的执行部分可以用 3 个任务来反映出任务执行的顺序。

    同步并行

    代码示例:

    func syncConcurrent() {
    
      let queue = DispatchQueue(label: "queue", attributes: .concurrent)
    
      print("--- begin ---")
    
      queue.sync {
          for i in 1...3 {
              print("--- 🅰️ \(i) ---\(Thread.current)")
          }
      }
    
      queue.sync {
          for i in 1...3 {
              print("--- 🅱️ \(i) ---\(Thread.current)")
          }
      }
    
      queue.sync {
          for i in 1...3 {
              print("--- 🅾️ \(i) ---\(Thread.current)")
          }
      }
    
      print("--- end ---")
    
    }
    

    打印结果

    --- begin ---
    --- 🅰️ 1 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅰️ 2 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅰️ 3 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅱️ 1 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅱️ 2 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅱️ 3 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅾️ 1 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅾️ 2 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- 🅾️ 3 ---<NSThread: 0x60400006f380>{number = 1, name = main}
    --- end ---
    

    任务是按照顺序逐个执行的,并且只有一个主线程在战斗

    异步并行

    代码示例

    func asyncConcurrent() {
    
        let queue = DispatchQueue(label: "queue", attributes: .concurrent)
    
        print("--- begin ---")
    
        queue.async {
            for i in 1...3 {
                print("--- 🅰️ \(i) ---\(Thread.current)")
            }
        }
    
        queue.async {
            for i in 1...3 {
                print("--- 🅱️ \(i) ---\(Thread.current)")
            }
        }
    
        queue.async {
            for i in 1...3 {
                print("--- 🅾️ \(i) ---\(Thread.current)")
            }
        }
    
        print("--- end ---")
    
    }
    

    打印结果 1

    --- begin ---
    --- 🅰️ 1 ---<NSThread: 0x604000078bc0>{number = 3, name = (null)}
    --- 🅱️ 1 ---<NSThread: 0x6080000747c0>{number = 4, name = (null)}
    --- 🅰️ 2 ---<NSThread: 0x604000078bc0>{number = 3, name = (null)}
    --- 🅾️ 1 ---<NSThread: 0x6040000793c0>{number = 5, name = (null)}
    --- 🅱️ 2 ---<NSThread: 0x6080000747c0>{number = 4, name = (null)}
    --- 🅰️ 3 ---<NSThread: 0x604000078bc0>{number = 3, name = (null)}
    --- end ---
    --- 🅾️ 2 ---<NSThread: 0x6040000793c0>{number = 5, name = (null)}
    --- 🅱️ 3 ---<NSThread: 0x6080000747c0>{number = 4, name = (null)}
    --- 🅾️ 3 ---<NSThread: 0x6040000793c0>{number = 5, name = (null)}
    

    打印结果 2

    --- begin ---
    --- 🅰️ 1 ---<NSThread: 0x604000069140>{number = 3, name = (null)}
    --- 🅱️ 1 ---<NSThread: 0x60800006aa40>{number = 4, name = (null)}
    --- 🅰️ 2 ---<NSThread: 0x604000069140>{number = 3, name = (null)}
    --- 🅱️ 2 ---<NSThread: 0x60800006aa40>{number = 4, name = (null)}
    --- 🅾️ 1 ---<NSThread: 0x60800006a900>{number = 5, name = (null)}
    --- 🅰️ 3 ---<NSThread: 0x604000069140>{number = 3, name = (null)}
    --- end ---
    --- 🅾️ 2 ---<NSThread: 0x60800006a900>{number = 5, name = (null)}
    --- 🅱️ 3 ---<NSThread: 0x60800006aa40>{number = 4, name = (null)}
    --- 🅾️ 3 ---<NSThread: 0x60800006a900>{number = 5, name = (null)}
    
    

    每一次运行的执行顺序不一定相同,任务的执行是交替的,一共新开了 3 个线程在战斗。并且从 begin 和 end 的位置可以看出,3 个任务被添加到队列之后就立刻开始了战斗。

    同步串行

    func syncSerial() {
    
        let queue = DispatchQueue(label: "queue")
    
        print("--- begin ---")
    
        queue.sync {
            for i in 1...3 {
                print("--- 🅰️ \(i) ---\(Thread.current)")
            }
        }
    
        queue.sync {
            for i in 1...3 {
                print("--- 🅱️ \(i) ---\(Thread.current)")
            }
        }
    
        queue.sync {
            for i in 1...3 {
                print("--- 🅾️ \(i) ---\(Thread.current)")
            }
        }
    
        print("--- end ---")
    
    }
    

    打印结果
    --- begin ---
    --- 🅰️ 1 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅰️ 2 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅰️ 3 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅱️ 1 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅱️ 2 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅱️ 3 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅾️ 1 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅾️ 2 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- 🅾️ 3 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
    --- end ---

    打印结果和同步并行一个样,都是只有主线程在战斗,并且按照顺序逐一执行。

    串行异步

    代码示例

    func asyncSerial() {
    
        let queue = DispatchQueue(label: "queue")
    
        print("--- begin ---")
    
        queue.async {
            for i in 1...3 {
                print("--- 🅰️ \(i) ---\(Thread.current)")
            }
        }
    
        queue.async {
            for i in 1...3 {
                print("--- 🅱️ \(i) ---\(Thread.current)")
            }
        }
    
        queue.async {
            for i in 1...3 {
                print("--- 🅾️ \(i) ---\(Thread.current)")
            }
        }
    
        print("--- end ---")
    
    }
    

    打印结果

    --- begin ---
    --- 🅰️ 1 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- 🅰️ 2 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- 🅰️ 3 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- end ---
    --- 🅱️ 1 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- 🅱️ 2 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- 🅱️ 3 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- 🅾️ 1 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- 🅾️ 2 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    --- 🅾️ 3 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
    

    任务是按顺序逐一进行的,开启了一条新的线程。

    主队列和全局队列的同步/异步

    同步

    DispatchQueue.global(qos: .default).async {
        print("I'm in global.")
        DispatchQueue.main.async {
            print("I'm back in main.")
        }
    }
    

    异步

    //Global Dispatch Queue
    //不会死锁,但代码顺序执行,后续代码需要等待前面代码执行完毕
    DispatchQueue.global(qos: .default).sync {
        print("I'm in global.")
        print("I'm still in global.")
    }
    print("I'm out.")
    
    //Main Queue
    //死锁。按照同步的执行顺序,一个任务需要等待前一个任务执行完毕,但是"I'm in main."是添加在"I'm out."之后,但是"I'm out"又需要等待"I'm in main."执行完毕之后再执行。所以彼此等待造成了死锁。
    DispatchQueue.main.sync {
        print("I'm in main.")
    }
    print("I'm out.")
    

    队列暂停和继续

    //暂停
    queue.suspend()
    //继续
    queue.resume()
    

    suspend() 和 resume() 是异步函数,在两个闭包之间生效。
    suspend() 会使得已经添加到 Dispatch Queue 但是还没有执行的任务在改行代码之后暂停执行,等到 resume() 之后才能继续执行。

    单次执行

    单次执行在多线程编程的使用过程中很常见,当然除了多线程之外也有一些其他的用途。
    在 Swift 3 之前,单次执行可以通过 dispatch_once 来实现,但是 Swift 3 把它废弃了... 查了一些资料发现要实现原来的 dispatch_once 可以通过下面几个方式:

    1. 全局常量
    let constant = SomeClass()
    
    1. 全局变量(带有立即执行的闭包构造器)
    var variable: SomeClass = {
        let constant = SomeClass()
        constant.oneProperty = "HeySiri"
        constant.oneMethod()
        return constant
    }()
    

    3. 未完待续...

    相关文章

      网友评论

          本文标题:iOS 多线程 GCD (Swift 3)

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