本文代码: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中所有场景都是围绕两种任务和两种队列来实现的,不同任务和队列的排列组合:
并发队列异步任务
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() } } }