美文网首页
iOS中的多线程技术

iOS中的多线程技术

作者: Must_Be_Sth | 来源:发表于2019-07-25 16:05 被阅读0次

    多线程

    多线程技术是iOS开发里十分常见的,下面会介绍GCD的常用多线程技术。首先简单了解一下几个概念:

    • 同步,异步:任务执行时是否开多线程,同步为单线程,异步为多线程
    • 串行,并发:任务执行的方式,串行为顺序执行,并发为同时执行
    • 队列:任务存放的地方,线程从队列里取出任务执行。分为串行队列和并发队列
    • 线程:执行任务的实际运作单位,主线程从主队列里取任务,更新UI必须在主线程完成

    GCD:

    GCD是操作最简单,也是我们最常用的多线程技术。在OC里是一套C语言API,在Swift里对其封装进一步简化,本文会使用Swift语法。

    一。主队列,全局队列

    先来看看这个GCD的最常用公式:

    DispatchQueue.global().async {   
        //long time operation
        DispatchQueue.main.async {   
            //update UI
        }
    }
    

    它分为以下几个步骤:

    • 通过DispatchQueue.global()获取全局队列
    • 在全局队列中async并发执行耗时操作,如下载
    • 通过DispatchQueue.main获取全局队列
    • 在全局队列中async并发执行更新UI的动作

    那么为什么要如此使用呢?

    • 首先,更新UI的操作必须放在主线程中,否则会报错。这里DispatchQueue.main是获取主队列,它是一个串行队列,而主线程要执行的任务就是到这个队列里去取的。
    • DispatchQueue.global()是获取系统提供的全局队列,该队列是并发队列。加入该队列的任务会开辟多条线程共同执行,就好像一个页面会下载显示很多张图片,用全局队列的话图片会一张张显示出来,而如果用主队列的话,所有图片下载完成才统一显示。
    • 这里不管是主队列还是全局队列都使用async,而没有使用sync是因为sync同步操作,相当于将所有操作都由主线程来执行,这会阻塞主线程,也就是UI上的卡顿。

    这里写了一个小实验比较上面的各种情况。(因为图片地址在公司服务器上,这里不公布代码,只看结果)。

    点击一个button,会Push到下一个页面,而下一个页面中会有一个CollectionView,共包含10个CollectionViewCell,每一个cell中都会下载并显示一张图片。

    • 第一张图用的是之前的标准公式,点击按钮后立即Push到下一个页面,并且图片也相继显示出来。

      GCDDemo1.gif
    • 第二张图是将DispatchQueue.global()换成了DispatchQueue.main()。可以看到,点击按钮后立即Push到下一个页面,所有图片下载完成后再一起显示,这是因为主队列是串行队列,所有的更新UI任务都排在了下载任务之后,因此要等所有的图片下载完成后,UI才会开始显示。

      [图片上传中...(GCDDemo3.gif-83a055-1564041778608-0)]
    • 第三张图是将DispatchQueue.global().async换成了DispatchQueue.global().sync。可以看到,点击按钮后卡顿了一会儿,再进入下一个页面。这是因为sync是同步操作,会阻塞主线程。

      GCDDemo3.gif

    由以上三张图可看出,常用公式是最合适的选择


    二。创建队列

    let queue = DispatchQueue(label: "com.demo.queue", qos: .background, attributes: .concurrent)
    
    • qos是指队列的优先级,从background到userinteractive,优先级从低到高
    • concurrent指定了该队列为并发队列,若不穿,则默认为串行队列

    自建的队列最终会被归入到主队列和全局队列中去,那么为什么还要创建它们呢,也是便于对一系列任务的管理。


    三。派发组DispatchGroup

    开发中也许会有这一种情况,我们要同时请求好几个接口后,再刷新UI。这种对多个异步请求的管理,用DispatchGroup最合适。

    用法如下

    let group = DispatchGroup()
    
    group.enter()
    //task1
    group.leave()
    
    group.enter()
    //task2
    group.leave()
    
    group.enter()
    //task3
    group.leave()
    
    group.notify(queue: DispatchQueue.main) {
        //update UI
        print("更新UI")
    }
    

    更新UI的操作会在task1,task2,task3这3个异步任务完成后再执行。


    四。信号量semaphore

    简单的说,在异步操作中,任务完成的顺序是不确定的。semaphore可以使得我们将异步操作按顺序同步完成。

    这里有一片博客,对其进行了详细的介绍


    五。屏障barrier

    想象有这样一个操作。从数据库里执行两次读的任务read1和read2,并发执行并没有什么问题。可如果要在read1和read2中间加入一个write1,要求read1读取的是write之前的数据,read2读取的是write之后的数据,那么应该如何处理呢?

    首先,如果write不能用普通的并发操作。因为并发队列的特性是无法确保read1,read2以及write的执行顺序,这可能会发生read2读取的是write之前的数据。

    这里我们需要用到barrier。顾名思义,它是作为一个屏障,隔开了read1和read2,在这个屏障内的进行write,也会保证,read1->write->read2这样的一个执行顺序

    DispatchQueue.global().async {
        //read1
    }
    DispatchQueue.global().async(flags: .barrier) {
        //write
    }
    DispatchQueue.global().async {
        //read2
    }
    

    六。延迟执行

    这里用GCD封装一个延迟执行

    func delay(_ timeInterval: TimeInterval, closure: @escaping ()->()) {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + timeInterval, execute: closure)
    }
    

    调用起来非常简单和明了

    delay(3) {
            //task  
    }
    

    值的一体的是,该延迟执行指的并不是在3秒后执行task,而指的是,在3秒后,将task加入主队列。所以当主队列有大量处理或者本身已经延迟的情况下,真正执行的时间会比3秒更长。不过一般来说,在主队列非阻塞的情况下,该方法还是算非常简洁有效的。


    七。创建单例

    得益于GCD中的dispatch_once,这个函数会让程序只执行一次,我们在OC中用它来实现单例。

    static dispatch_once_t pred;
    dispatch_once(pred, ^{
        //init
    });
    

    不过为了防止单例类通过alloc或者new的方法实例化,实际上要写的代码还有很多,相比较下来Swift的单例实现就要简单很多。

    class Singleton {
        static let shared = Singleton()
        private init() {}
    }
    

    八。死锁

    死锁一般是指同步任务的相互等待,造成程序崩溃。

    例如,我们在ViewController的ViewDidLoad里加入这一句,程序执行到这里立刻就会崩溃。

    DispatchQueue.main.sync {}
    

    因为ViewDidLoad是从主线程执行,而DispatchQueue.main.sync {}也是在主线程里同步执行。ViewDidLoad需要等待DispatchQueue.main.sync {}完成,但ViewDidLoad又是先加入主队列的,因此DispatchQueue.main.sync {}要执行必须先等ViewDidLoad执行完毕。因此他们相互等待,造成死锁。

    相关文章

      网友评论

          本文标题:iOS中的多线程技术

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