Swift3 GCD的基本用法(一) - 任务和队列、服务优先级、信号量

本文代码:https://github.com/NinoWang/MultithreadingDemo/tree/master


多线程的知识,网上有无数文章来讲述,但真正理解起来还是有点绕的,还是要靠实践才能真正理解,本文以代码为主,少量叙述为辅和大家来捋一下。建议直接下载源码进行理解,不理解的地方在来文章里找答案。话少说,开捋。


基本概念

说到GCD,通常与相似功能的Operation Queue进行比较理解。

GCD是使用C语言构成的API,而Operation Queue是具体的Objc对象;GCD是使用block的形式管理队列中的任务,而Operation Queue是直接把队列和任务作为具体的对象进行操作。


任务和队列

任务分为同步任务(sync异步任务(async两种。

两者的区别在于,异步任务具备开辟新线程的能力,而同步任务不具备该能力。


队列是执行任务的容器,遵循先进先出(FIFO)的原则,GCD中队列分为串行队列(Serial Dispatch Queue并发队列(Concurrent Dispatch Queue两种。

两者的区别在于,并发队列可以同时执行多个任务(自动开启多个线程),而串行队列只能按顺序逐个执行任务。


另外还有两个特殊的子分类的队列:全局队列(global queue)和主队列(main queue)。

全局队列:并发队列的一种,用来执行较耗时的操作。

主队列:串行队列的一种,只能在主线程中进行,只有主线程空闲的时候才能被执行,用来刷新UI


可以说GCD中所有场景都是围绕两种任务和两种队列来实现的,不同任务和队列的排列组合:


Swift3 GCD的基本用法(一) - 任务和队列、服务优先级、信号量_第1张图片


并发队列异步任务

func conAsync() {
        let concurrentQueue = DispatchQueue(label: "Concurrent", attributes: .concurrent)
        for i in 0...10 {
            concurrentQueue.async {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

结果为无序

串行队列异步任务

func serAsync() {
        let serialQueue = DispatchQueue(label: "Serial")
        for i in 0...10 {
            serialQueue.async {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

结果为有序

主队列异步任务

func mainAsync() {
        let mainQueue = DispatchQueue.main
        for i in 0...10 {
            mainQueue.async {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

结果为有序

全局队列异步任务

func globalAsync() {
        let globalQueue = DispatchQueue.global()
        for i in 0...10 {
            globalQueue.async {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

结果为无序

并发队列同步任务

func conSync() {
        let concurrentQueue = DispatchQueue(label: "Concurrent", attributes: .concurrent)
        for i in 0...10 {
            concurrentQueue.sync {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

结果为有序

串行队列同步任务

func serSync() {
        let serialQueue = DispatchQueue(label: "Serial")
        for i in 0...10 {
            serialQueue.sync {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

结果为有序

主队列同步任务

func mainSync() {
        let mainQueue = DispatchQueue.main
        for i in 0...10 {
            mainQueue.sync {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

死锁造成程序假死

全局队列同步任务

func globalSync() {
        let globalQueue = DispatchQueue.global()
        for i in 0...10 {
            globalQueue.sync {
                print("this is NO.\(i), current thread name is \(Thread.current)")
            }
        }
    }

结果为有序

线程间通讯-从子线回到主线程

iOS开发中,主线程主要用来处理UI层面的任务,诸如:点击、拖拽、滚动等。而比较耗时的任务则放到子线程中,诸如:数据请求、文件下载上传等。这个时候就需要使用到线程之间的通讯。

let globalQueue = DispatchQueue.global()
        
        globalQueue.async {
            if let url = URL.init(string: "https://placebeard.it/200/150") {
                do {
                    let imageData = try Data(contentsOf: url)
                    let image = UIImage(data: imageData)
                    
                    DispatchQueue.main.async {
                        self.imgView.image = image
                        self.imgView.sizeToFit()
                    }
                    
                } catch {
                    print(error)
                }
            }
            
        }

服务优先级(Qos)

这里的服务优先级决定了对一个任务分配资源的大小,并非绝对的执行顺序。swift3中Qos共有6个级别,优先级从高到低依次为userInteractive、userInitiated、default、utility、background、unspecified。

func QoS() {
        // 优先级从高到低 userInteractive、userInitiated、default、utility、background、unspecified
        // 指定Qos 这里分别用三种方式指定
        // 方式1
        let userInteractiveQueue = DispatchQueue(label: "userInteractive", qos: .userInteractive)
        let defaultQueue = DispatchQueue(label: "default", qos: .default)
        let conQueue = DispatchQueue(label: "con", attributes: .concurrent)
        for i in 0...5 {
            userInteractiveQueue.async {
                print("userInteractive ====> \(i)")
            }
            
            defaultQueue.async {
                print("defaultQueue ====> \(i)")
            }
            
            // 方式2
            DispatchQueue.global(qos: .unspecified).async {
                print("unspecified ====> \(i)")
            }
            DispatchQueue.global(qos: .userInitiated).async {
                print("userInitiated ====> \(i)")
            }
            
            // 方式3
            conQueue.async(qos: .utility) {
                print("utility ====> \(i)")
            }
            conQueue.async(qos: .background) {
                print("background ====> \(i)")
            }
        }
    }

信号量(semaphore)

先看下百度百科的描述信号量的例子:

以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。

在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

简单来说,信号量起到对多线程调用资源的监管作用。

DispatchSemaphore(value:):用于创建信号量,可以指定初始化信号量计数值,这里我们默认1

semaphore.wait():会判断信号量,如果为1,则往下执行。如果是0,则等待。

semaphore.signal():代表运行结束,信号量加1,有等待的任务这个时候才会继续执行。

func semaphore() {
        let semaphore = DispatchSemaphore(value: 1)
        for i in 0...10 {
            DispatchQueue.global().async {
                semaphore.wait()
                print("\(i)")
                
                semaphore.signal()
            }
        }
    }

续篇:Swift3 GCD的基本用法(二) - 队列的循环/挂起/恢复、其他常用方法


你可能感兴趣的:(----------,iOS,----------,iOS,swift,iOS,理论)