Swift

Swift3.0 DispatchQueue-对GCD的改写

2017-04-21  本文已影响207人  Supremodeamor

在iOS中使用多线程的技术包括NSTread、NSOperationQueue、Grand Central Dispatch(GCD) 三种。NSThread继承于NSObject类,创建方法略显繁琐,且是在ObjectIve-C中使用的。NSOperationQueue是对GCD的面向对象的封装,在日常开发中,GCD是最为快捷简便的,也是我日常使用最多的,而在Swift3.0中,基于 Swift语法封装的DispatchQueue给Swift带来了更为方便的使用,今天就来扒一扒这个DispatchQueue

DispatchQueue-基本概念

一、串行和并发

DispatchQueue 即执行任务的队列,分为串行队列(Serial Dispatch Queue)和并发队列(Concurrent Dispatch Queue)。串行队列中的任务是顺序执行的,即先放进去的任务先执行,后放入的人物后执行。并发队列的任务则可以在后台中一起执行。

二、同步和异步

同步(sync)和异步(async)的概念很容易和串行并发的概念混淆,这里特别说明一下:同步异步是线程之间的执行方式,串行并发是单一队列内多个任务的执行方式。
可以先看个案例:

override func viewDidLoad() {
        super.viewDidLoad()
    
        let queue1: DispatchQueue = DispatchQueue(label: "com.artron.myqueue")
       let queue2: DispatchQueue = DispatchQueue(label: "com.artron.myqueue")
        queue1.sync {
            for i in 0..<10 {
                print("🌧", i)
            }
        }
        queue2.async {
            for i in 10..<20 {
                print("🔥", i)
            }
        }
        for i in 100..<110 {
            print("💧", i)
        }
}

viewDidLoad方法,也就是主线程中,创建任务队列,先同步遍历,再异步遍历,然后再主线程遍历,看下运行结果:

先同步后异步

下面再看下先异步后同步的情况:

override func viewDidLoad() {
        super.viewDidLoad()
    
        let queue1: DispatchQueue =     DispatchQueue(label: "com.artron.myqueue")
       let queue2: DispatchQueue =     DispatchQueue(label: "com.artron.myqueue")
        queue1.async {
            for i in 10..<20 {
                print("🔥", i)
            }
        }
        queue2.sync {
            for i in 0..<10 {
                print("🌧", i)
            }
        }
        for i in 100..<110 {
            print("💧", i)
        }
    }
先异步后同步

两个实验结果可以得出结论:
1,同步执行时,只有该队列内任务完成时,才会执行下一个队列;
2,异步执行时并不影响下一个队列的执行。
那么为什么会出现这两种不同的方式呢?也就是二者的使用情景有什么不同?
异步执行因其不阻塞主线程,可以将任务周期长(如网络请求)、CPU占用率高的操作放在后台执行。而当不同线程需要操作相同变量时,为了避免发生数据操作冲突和危险,同步线程就可以派上用场了。

3,任务项(WorkItem)

任务项就是代码块,即队列里要执行的任务代码,一个队列里可以有多个任务项,任务项之间的执行方式就是或串行、或并发。

4,优先级和Quality of Service (Qos)

除了简单的同步异步影响队列的执行,优先级会直接决定队列的执行顺序。queueWithQos()包含了队列的重要程度和优先级信息,Swift中用枚举描述:

/// qos_class_t
public struct DispatchQoS : Equatable {

    public let qosClass: DispatchQoS.QoSClass

    public let relativePriority: Int

    public static let background: DispatchQoS

    public static let utility: DispatchQoS

    public static let `default`: DispatchQoS

    public static let userInitiated: DispatchQoS

    public static let userInteractive: DispatchQoS

    public static let unspecified: DispatchQoS

    public enum QoSClass {

        case background

        case utility

        case `default`

        case userInitiated

        case userInteractive

        case unspecified

        public init?(rawValue: qos_class_t)

        public var rawValue: qos_class_t { get }
    }

  public init(qosClass: DispatchQoS.QoSClass, relativePriority: Int)
}

优先级顺序:userInteractive> userInitiated> default> utility> background> unspecified

以上概念理解之后,再使用DispatchQueue就很容易了。

DispatchQueue-API解析

  1. 便利构造器

    public convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)
    

参数解释:
label: 队列名称,最好是写有意义名字,能一看就知道这个队列要执行什么任务;
qos: 队列重要性和优先级(.userInteractive .userInitiated .default .utility .background .unspecified)
attributes: 可填写两个属性:.concurrent表明队列是并发队列,.initiallyInactive表明队列需要手动开启。不填写时默认队列串行、自动执行。
autoreleaseFrequency: 官方文档是说当设为.workItem时,所有异步任务提交的代码块会被封装成独立的任务,在同步执行的队列则不受影响。
target : 目标队列
后两个参数的意义我还不太理解,希望有人看到可以指教一下。
示例:

let autoQueue: DispatchQueue = DispatchQueue(label: "defaultQueue", qos: .userInitiated)
    autoQueue.async {
        print("I will auto activate")
    }
    
let initiallyInactiveQueue : DispatchQueue = DispatchQueue(label: "initiallyInactiveQueue", qos: DispatchQoS.userInitiated, attributes: [.concurrent, .initiallyInactive])
    initiallyInactiveQueue.async {
        print("I need be activate manual")
    }
    initiallyInactiveQueue.activate()

注:手动开始线程是为了在特定场景下执行特定任务,此处只是为了演示用法。

2.执行方法

public func sync(execute workItem: DispatchWorkItem),
public func async(execute workItem: DispatchWorkItem),
public func sync(execute block: () -> Swift.Void),
public func async(execute block: () -> Swift.Void)

这四个方法其实就是两个方法,只不过一个是直接执行block代码,一个是执行任务项。同样的延迟执行方法如下:

public func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem),
public func asyncAfter(deadline: DispatchTime, qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, execute work: @escaping @convention(block) () -> Swift.Void)

    let delayQueue = DispatchQueue(label: "delayQueue");
    // 延迟2s执行
    delayQueue.asyncAfter(deadline: .now() + .seconds(2)) { 
        print(Date())
    }

3.DispatchGroup
DispatchGroup就是把队列放到一个组里统一管理,这些队列执行的任务一般是有关联的,示例:

    let group = DispatchGroup()
    let queue1 = DispatchQueue(label: "work1")
    queue1.async(group: group) {
        print("干完work1")
        sleep(2)
    }
    group.wait()
    let queue2 = DispatchQueue(label: "work2")
    queue2.async(group: group){
        print("干完work2")
    }
    group.notify(queue: DispatchQueue.main) {
        print("都干完了,可以下班了")
    }

分析:queue1queue2是异步执行的两个队列,queue1中令系统沉睡2s,方法group.wait()的作用是让queue1执行完毕后再执行后续队列(这里是queue2),否则queue2异步执行。
group.notify()方法是在group内的队列都执行完毕后才会执行。

4,获取主线程队列

 let mainQueue = DispatchQueue.main
 mainQueue.async { 
        print("其实还是主线程")
    }

5,全局队列

DispatchQueue.global().async {
    print("全局队列")
}
上一篇下一篇

猜你喜欢

热点阅读