说到多线程,我们必须明白两个概念:线程与队列。我把线程理解为车间的流水线,而队列则决定了每条流水线的工作方式。我们可以往队列中加入任务,并决定该队列在什么线程执行。
主队列异步执行
DispatchQueue.main.async { print(Thread.current) DispatchQueue.main.async { print(Thread.current) sleep(2) print(1) } print(2) DispatchQueue.main.async { print(Thread.current) print(3) } sleep(1) }
- 打印结果:
- 分析:上述代码看似异步执行开了子线程,实则不然,DispatchQueue.main.async实际上队列中的任务还是在主线程执行,也就是同步执行。而它与DispatchQueue.main.sync的区别就是它不会造成死锁。
由于主队列是串行队列,根据先进先出的原则,上述代码的执行顺序是:将sleep(2)、print(1)加入队列->print(2)->将print(3)加入队列->sleep(1)->sleep(2)->print(1)->print(3)
串行队列异步执行
let serialQueue = DispatchQueue(label: "serial_queue") serialQueue.async { print(Thread.current) serialQueue.async { sleep(2) print(Thread.current) print(1) } print(2) serialQueue.async { print(3) print(Thread.current) } sleep(1) }
-
打印结果:
-
分析:为什么要自定义串行队列与主队列分开来?我们知道主队列就是一个串行队列,而它与普通串行队列在异步执行时有什么区别呢?我们可以尝试打印各个线程中的Thread.current,从结果可以看出,自定义串行队列在异步执行时是创建了支线程的,且因为串行的原因只会创建一个支线程。
同是串行队列,它的执行顺序同上。
并发队列异步执行
let concurrentQueue = DispatchQueue(label: "concurrent_quque", attributes: .concurrent) concurrentQueue.async { print(Thread.current) concurrentQueue.async { print(Thread.current) sleep(2) print(1) print(4) print(5) print(6) print(7) } print(2) concurrentQueue.async { print(Thread.current) print(3) } sleep(1) }
- 打印结果:
- 分析:从结果可以看出,并发队列异步执行会开多个线程,其数量等于concurrentQueue.async的调用次数。
并发队列的特点是它在异步执行时,队列中的任务可以无限接近同时执行。那么问题来了:经过试验,无论我打印多少次,这两段代码都没有体现出并发执行的特性,因为它打结果并没有发生改变。
再观察一下代码,可以发现:第一段异步执行代码中sleep(2)让线程睡了2s,我们把它注释。
多打印几次:
因为print(Thread.current)也算一个任务,我们注释掉。
结论:
- 对于主队列,同步、异步执行都不会开线程;
- 对于主队列以外的队列,同步执行不会开线程,异步执行会开线程;
- 一般来说,开线程的数量取决于.async的调用次数;
- 无论是串行还是并发,队列中的任务在一个async{ }中都是顺序执行的;
- 任务并发是指并发队列在异步执行时多个async{ }中的任务无限接近同时执行。
- 串行队列异步只会创建一个子线程