GCD 对于使用者来说,是没有线程的概念的,只有派发队列(DispatchQueue),所有任务都是以 block 方式提交到队列上的,GCD 会根据 CPU 的使用情况,自动创建线程池来执行任务,开发者无法参与到队列的管理中,队列是由系统自动管理的,管理的模式是FIFO(先进先出),先加入的 block 会被先执行。
队列分为以下两种:
1、串行队列(serial):不管调用的是 async 还是 sync,任务都是做完一个才到下一个,顺序执行。
2、并发队列(concurrent):任务是并发一起执行。
async 和 sync 的意义:
1、async:提交一段 block 到队列,立刻返回,不会阻塞当前线程。
2、sync:提交一段 block 到队列,会阻塞当前线程,直到 block 执行完毕,当前线程才可以继续执行。
通过具体的例子,能对 GCD 的运行有很好的理解(下面例子都是在主线程运行的):
/// 例子一,同一串行队列
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async {
print("1")
serialQueue.async {
Thread.sleep(forTimeInterval: 2) /// 休眠2秒
print("2")
}
print("3")
}
/// 执行代码,打印为:1,3,2
外层 async,称作 blockA,内层 async,称作 blockB。
1 - 程序执行,blockA 被提交到串行队列中,不阻塞当前线程;
2 - 队列执行 blockA,打印字符串1,碰到 blockB, 提交 blockB 到队列中,不阻塞当前线程,打印字符串3,此时 blockA 执行完毕,退出了队列,队列只剩下 blockB 未执行;
3 - 队列执行 blockB, 休眠2秒后打印字符串2,blockB 执行完毕,退出队列,程序全部执行完毕。
/// 例子二,同一串行队列
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async {
print("1")
serialQueue.sync {
Thread.sleep(forTimeInterval: 2) /// 休眠2秒
print("2")
}
print("3")
}
/// 执行代码,打印为:1
外层 async,称作 blockA,内层 sync,称作 blockB。
1 - 程序执行,blockA 被提交到串行队列中,不阻塞当前线程;
2 - 队列执行 blockA,打印字符串1,碰到 blockB, 提交 blockB 到队列中,同步阻塞当前线程,此时队列中有 blockA 和 blockB 存在;
3 - 由于队列是串行队列,需执行前面的 block,才会执行后面的 block,就是说必须执行完 blockA 的代码,才会执行 blockB,而 blockA 被 blockB 阻塞住了,需要等待 blockB 执行完毕才能继续执行,blockB 想要执行,必须等队列前面的 blockA 执行完毕退出队列,才能执行,这意味着 blockB 没法执行,blockA 也没法执行毕,死锁了。
/// 例子三,同一并发队列
let concurrent = DispatchQueue(label: "concurrent", attributes: .concurrent)
concurrent.async {
print("1")
concurrent.sync {
Thread.sleep(forTimeInterval: 2) /// 休眠2秒
print("2")
}
print("3")
}
/// 执行代码,打印为:1,2,3
外层 async,称作 blockA,内层 sync,称作 blockB。
1 - 程序执行,blockA 被提交到串行队列中,不阻塞当前线程;
2 - 队列执行 blockA,打印字符串1,碰到 blockB, 提交 blockB 到队列中,同步阻塞当前线程,此时队列中有 blockA 和 blockB 存在;
3 - 队列是并发队列,是一起执行 block 的,由于 blockB 阻塞了 blockA,blockA 需要等待 blockB 执行完毕才能继续执行,休眠2秒打印字符串2,blockB 执行完毕退出队列,blockA 继续执行,打印字符串3,退出队列,程序全部执行完毕。
/// 例子四,不同串行队列
let serialQueue1 = DispatchQueue(label: "serial1")
let serialQueue2 = DispatchQueue(label: "serial2")
serialQueue1.async {
print("1")
serialQueue2.sync {
Thread.sleep(forTimeInterval: 2) /// 休眠2秒
print("2")
}
print("3")
}
/// 执行代码,打印为:1,2,3
外层 async,称作 blockA,内层 sync,称作 blockB。
1 - 程序执行,blockA 被提交到串行队列1中,不阻塞当前线程;
2 - 队列执行 blockA,打印字符串1,碰到 blockB, 提交 blockB 到串行队列2中,同步阻塞当前线程,此时队列1中有 blockA ,队列2中有 blockB 存在;
3 - 由于不属于同一串行队列,所以 blockB 阻塞的是队列2,而不是队列1,blockA 虽然需要等待 blockB 执行完毕才能继续执行,但是却不会像例子二一样,造成死锁现象,而是等待队列2中的 blockB 执行完毕后,继续执行下去,所以先打印字符串2后,再打印字符串3。
通过上面几个例子,可以很清楚的知道队列是怎样运行的,首先要弄清楚是什么队列,是串行还是并发,之后在弄清楚是 async 还是 sync,是在同一个队列还是不同队列,再依据逻辑梳理一下,就能搞清楚会不会造成死锁。
网友评论