美文网首页
Swift多线程开发 - 2. GCD 队列和线程

Swift多线程开发 - 2. GCD 队列和线程

作者: JaiUnChat | 来源:发表于2020-04-04 10:54 被阅读0次

    一个App是 一个进程,一个进程拥有 多个线程。GCD队列负责分配任务到不同的线程上。

    多线程的优点:

    • 运行更快. 多线程可以并发处理任务,速度比串行更快
    • 响应. 如果全部任务都在主线程进行,那么在出现比较耗时的任务的时候,用户会明显感觉到卡顿造成较差的用户体验。
    • 优化的资源消耗. 多线程经过操作系统高度优化的,使得线程切换等消耗极小。

    1. 基础应用

    串行

     let label = "wang.junshuo.example"
     let queue = DispatchQueue(label: label)
    

    并发

     let label = "wang.junshuo.example"
     let queue = DispatchQueue(label: label, attributes: .concurrent)
    

    在推荐的规范中,label的命名采用BundleID似的反域名的方式加上功能名称,比如com.company.app

    主队列

    应用启动的时候会自动创建负责用户界面的 串行 主队列。
    由于使用频率很高,它可以方便的通过DispatchQueue.main类变量访问。

    2. 优先级(Quality of service)

    不同重要程度的任务通过QoS可以使用不同程度的系统资源和电量,达到高效完成

    比如取一个.userInteractive级别的全局队列
    let queue = DispatchQueue.global(qos: .userInteractive)

    级别

    系统预设的优先级有六个。

    a. .userInteractive

    这个级别的任务设计为处理直接与用户交互相关的任务。比如界面更新计算,动画等。
    如果这个任务不马上完成,用户界面看起来就会卡住。所以提交到这个队列任务需要立即完成.

    b. .userInitiated

    这个级别的任务设计为处理需要立即发生但是可以异步完成。比如从用户点击查看一个文档,或者从本地数据库读取信息等。
    这里的任务也期望短时间或几秒内迅速完成。

    c. .default

    这个选项是默认值,是作为没有明确设定优先级的时候的缺省值。最好给每个任务都设定一个级别而不是使用这个值。

    c. .utility

    这个级别的任务设计为用于长时间运行的计算等。比如联网或连续数据馈送。
    任务会在响应速度和性能与能源效率之间取得平衡。这里的任务可能需要几秒钟到几分钟。

    d. .background

    这个级别的任务设计为用于花费长时间并且用户不在乎它什么时候完成的。比如数据库维护,同步远程服务器和备份。
    这里的任务将专注于能源效率而不是速度,那些用大量时间(几分钟或更长)的工作应该放这里。

    e. .unspecified

    这个选项是用于兼容旧版API,因为有些旧版API可能会使线程超出QoS范围。所以不要使用这个值。

    优先级推断

    如果加入队列的任务优先级比队列高,那么队列中以及其包含的所有任务的优先级也会自动提升到这个优先级。

    3. 添加任务

    比如添加一个网路请求任务然后刷新

    DispatchQueue.global(qos: .utility).async { [weak self] in
        guard let self = self else { return }
        // 网络请求代码
    
        // 切换到主线程刷新任务
        DispatchQueue.main.async {
            self.textLabel.text = "JuHub又更新了哇!" 
        }
    }
    

    注意点

    上述代码有两点值得注意

    a. Weak Self

    GCD是不会造成循环引用的, 但是在GCD的闭包中强引用self会使得self更晚被释放(直到网络请求完成才会释放)。
    比如在一个将要被dismissviewController中网络请求还没有完成,则弱引用selfviewController会被直接释放(如果没有其它循环引用),而强引用的viewController直到网络请求完成才会释放。

    具体要不要弱引用self完全取决于业务需求

    b. 主线程刷新

    通常我们都是在网络请求完之后异步刷新主线程。使用sync要非常注意,因为一旦错误的调用比如在当前线程同步任务中访问同步队列中的资源,那么当前线程就会造成死锁问题。

    在主线程添加同步任务会造成主线程死锁

    更进一步的,在上一篇中我们讲过无法确定队列中的任务最终会被分配到哪个线程,所以在你创建了一个自定义队列然后添加同步任务的时候,这个同步任务可能还是在主线程执行。而此时如果在这个自定义队列里DispatchQueue.main.sync(){}就会造成死锁

    func test() {
        let queue = DispatchQueue(label: "wang.junshuo.example.testQueue")
    
        print("1\(Thread.current)") // Main Thread
    
        queue.sync {
            print("2\(Thread.current)") // Main Thread 
            DispatchQueue.main.sync {
                print("3\(Thread.current)") // Main Thread. 死锁
            }
        }
    }
    

    所以执行同步任务的时候要十二分的谨慎。


    作者博客地址

    系列文章链接

    相关文章

      网友评论

          本文标题:Swift多线程开发 - 2. GCD 队列和线程

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