美文网首页
Swift: GCD基本知识与 Operation Queue

Swift: GCD基本知识与 Operation Queue

作者: 婉卿容若 | 来源:发表于2016-06-13 11:41 被阅读1269次

    GCD的三种queue

    • main queue: 主线程队列.她是一个串行队列,一般用于更新UI
    • global queue:全局队列.她是一个并行队列
    • custom queue:自定义队列.定义方式:
      方式一:
     //串行队列
      let serail_queue = dispatch_queue_create("com.zhengwenxiang", DISPATCH_QUEUE_SERIAL)
    

    方式二:

    //并行队列
        let concurrent_queue = dispatch_queue_create("com.zhengwenxiang", DISPATCH_QUEUE_CONCURRENT)
    

    这是全局队列

     let global_queue = dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
            //
        }
    

    DISPATCH_QUEUE_SERIAL即串行队列,DISPATCH_QUEUE_CONCURRENT即并行队列


    各种队列讨论

    • 串行同步
    func testSerailQueueWithSync() -> Void {
            for i in 0..<10 {
                dispatch_sync(self.serail_queue, {
                    print("index=\(i)")
                    print("current thread is \(NSThread.currentThread())")
                })
            }
            
            print("My main Thread is:\(NSThread.currentThread())")
        }
    
    串行同步.jpeg

    结论:
    1.index值按顺序输出,符合串行队列特征
    2.Runing on main Thread: 在最后输出,会阻塞主线程
    3.所有线程地址相同都是主线程地址,说明串行同步不会创建新线程,会直接使用当前线程执行队列中的任务

    • 串行异步
    func testSerialQueueWithAsync() -> Void {
            for i in 0..<10 {
                dispatch_async(self.serail_queue, { 
                    print("index=\(i)")
                    print("current thread is \(NSThread.currentThread())")
                })
            }
            
            print("My main Thread is:\(NSThread.currentThread())")
        }
    
    串行异步.jpeg

    总结:
    1.index值按顺序输出,符合串行队列特征
    2.current thread地址始终相同,但与主线程地址不同,说明是同一线程执行的
    3.Runing on main Thread:随机出现,并没有在最后出现,与第二点相结合说明串行异步在主线程外只创建一个新线程,并且新线程不会阻塞主线程

    • 并行同步
     func testConcurrentQueueWithSync() -> Void {
            for i in 0..<10 {
                dispatch_sync(self.concurrent_queue, {
                    print("index=\(i)")
                    print("current thread is \(NSThread.currentThread())")
                })
            }
            
            print("My main Thread is:\(NSThread.currentThread())")
        }
    
    
    并行同步.png

    总结:
    与串行同步输出结果相同,依然是只有一条主线程

    • 并行异步
    func testConcurrentQueueWithAsync() -> Void {
            for i in 0..<10 {
                dispatch_async(self.concurrent_queue, {
                    print("index=\(i)")
                    print("current thread is \(NSThread.currentThread())")
                })
            }
            
            print("My main Thread is:\(NSThread.currentThread())")
        }
    
    并行异步.jpeg

    总结:
    1.输出结果是乱序的,且线程地址不同,说明异步函数中的输出语句是并发的,由多个线程执行
    2.My main Thread is:没有在最后输出,说明主线程没有被阻塞


    结论:

    • 同步:无论是串行还是并行都不创建新线程,都是在当前线程上按顺序执行
    • 异步:
      1.串行中,会创建一条线程用于执行循环语句;并行中,每执行一次任务都会创建一个线程(显然,由于机器性能问题,开辟的线程总数量是有限制的),这就导致执行顺序总是乱序的
      2.串行中循环语句按顺序执行,先进先出(只会创建一条线程来执行惹怒),主线程随机出现,不会阻塞主线程
      3.并行中循环语句随机执行,主线程随机出现,不会阻塞主线程

    dispatch_barrier

    这个之前见过,但没懂.今天看了码农界吴彦祖的讲解才理解.在这里写出了是为了做个笔记.

    barrier.png

    看图(图是搬过来的咯).
    在一个并行队列中,有多个线程和一个dispatch_barrier,而barrier的作用就是保证barrier之前的线程(block0...block3)执行完成之后才能执行barrier之后的线程(block5/block6).

    这是一段测试代码

     func testDispatchBarrierWithConCurrentQueue() -> Void {
            for i in 0..<10 {
                dispatch_async(self.concurrent_queue, {
                    print("index=\(i)")
                })
            }
            
            for j in 0...1000 {
                dispatch_barrier_sync(concurrent_queue, {
                    if j == 999{
                        print("barrier finishesd")
                        print("current thread is \(NSThread.currentThread())")
                    }
                })
            }
            
            print("My main Thread is:\(NSThread.currentThread())")
            
            for i in 10..<20 {
                dispatch_async(self.concurrent_queue, {
                    print("index=\(i)")
                })
            }
        }
    

    执行结果:

    barrier_sync.jpeg

    从图中可以看出,barrier确实起到了他的作用.但我们可以看到,barrier也阻塞了主线程.
    还好,ios给我提供了两种方法,dispatch_barrier_syncdispatch_barrier_async
    当我用使用异步方法是就不会阻塞主线程了
    这是一段测试代码

     func testDispatchBarrierWithConCurrentQueue() -> Void {
            for i in 0..<10 {
                dispatch_async(self.concurrent_queue, {
                    print("index=\(i)")
                })
            }
            
            for j in 0...1000 {
                dispatch_barrier_async(concurrent_queue, {
                    if j == 999{
                        print("barrier finishesd")
                        print("current thread is \(NSThread.currentThread())")
                    }
                })
            }
            
            print("My main Thread is:\(NSThread.currentThread())")
            
            for i in 10..<20 {
                dispatch_async(self.concurrent_queue, {
                    print("index=\(i)")
                })
            }
        }
    

    执行结果:

    barrier_async.jpeg

    dispatch_barrier_async会开辟一条新线程
    同时,barrier一般只与并行队列一起使用,因为串行队列本身就是按顺序执行,没有使用barrier的必要


    operation queue

    • operation queue是基于 GCD 的面向对象的封装
    • operation queue也是一个队列,在 iOS中用 NSOperationQueue 表现
    • NSOperationQueue:队列,添加到其中的任务都会并行执行;不遵循先进先出原则.不是一个 closure, 而是一个NSOperation 类(抽象类,不能直接生成一个对象),所以 iOS 基于 NSOperation实现了两个具象类
      1.NSBlockOperation
      2.NSInvicationOperation: iOS8.1后废除
    使用方法一

    直接通过 addOperationWithBlock方法添加一个闭包

     let queue = NSOperationQueue() // operation 队列
            queue.addOperationWithBlock {
                let img1 =  Downloader.downloadImageWithUrl(self.imageUrls[0])
                NSOperationQueue.mainQueue().addOperationWithBlock({
                    self.imageView01.image = img1
                    self.imageView01.clipsToBounds = true
                })
            }
    
    使用方法二

    通过 NSBlockOperation新家一个任务,再addOperation添加任务到队列

     let op2 = NSBlockOperation {
                let img2 =  Downloader.downloadImageWithUrl(self.imageUrls[1])
                
                NSOperationQueue.mainQueue().addOperationWithBlock({
                    self.imageView02.image = img2
                    self.imageView02.clipsToBounds = true
                })
            }
     queue.addOperation(op2)
    
    任务完成

    通过第二种方式新建任务,我们任务完成后允许做一些其他操作

     op2.completionBlock = {print("image2 downloaded!")}
    

    这一步需要在将任务加入队列(即queue.addOperation(op2))前
    For Instance

     op2.completionBlock = {print("image2 downloaded!")}
     queue.addOperation(op2) // 加入队列
    
    任务控制

    我们可以对任务之间新建关系,以控制他们之间的完成顺序

    op3.addDependency(op4) // 关联---op3始终在op4后完成
    

    这一步需要在将任务加入队列前
    For Instance

      // 任务控制
            op3.addDependency(op4) // 关联---op3始终在op4后
            op2.addDependency(op3)
            
            // 添加关系后重新添加任务..记得注释掉上面的添加操作
            queue.addOperation(op4)
            queue.addOperation(op3)
            queue.addOperation(op2)
    
    

    ####### 取消任务

    对于任务我们可以进行取消操作.操作后,对于已完成的任务并不会做任何改变,对于未完成的任务将做取消处理.
    同事,对于已建立关联的任务.前面的任务取消了,其后的任务也自然不会执行
    例如,上面代码,op3取消成功了, op2就不会再执行了

    取消任务代码

    self.queue.cancelAllOperations()
    

    下载地址

    demo地址

    相关文章

      网友评论

          本文标题:Swift: GCD基本知识与 Operation Queue

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