GCD 串行队列调度异步和同步
Posted
技术标签:
【中文标题】GCD 串行队列调度异步和同步【英文标题】:GCD Serial Queue dispatch async and sync 【发布时间】:2021-10-24 20:23:55 【问题描述】:我对 GCD 有一些疑问。
代码 sn-p 1
serialQ.sync
print(1)
serialQ.async
print(2)
serialQ.async
print(3)
代码 sn-p 2
serialQ.async
print(1)
serialQ.async
print(2)
serialQ.sync
print(3)
我在操场上运行了它们,发现 Code sn-p 2 产生了死锁,而 Code sn-p 1 运行良好。我已经阅读了很多关于 GCD 的内容,并开始研究这些概念。任何人都可以提供相同的详细解释吗? PS : serialQ 是一个串行队列
据我了解,
串行队列 - 一次只生成一个线程,一旦该线程被释放,它就会被占用或空闲来执行其他任务
串行队列调度同步 - 阻塞调度串行队列的调用者线程并在该线程上执行任务。
串行队列调度异步 - 不会阻塞调用者线程,事实上它在不同的线程上运行并保持调用者 线程正在运行。
但对于上述查询,我无法得到正确的解释。
【问题讨论】:
【参考方案1】:您在已经在同一个队列上执行的块内调用sync
。这总是会导致死锁。 sync
相当于说“现在执行并等待它返回”。由于您已经在该队列上执行,因此该队列永远无法用于执行 sync
块。听起来您正在寻找递归锁,但这不是队列的工作方式。一般来说,它也可以说是一种反模式。我在这个答案中对此进行了更多讨论:How to implement a reentrant locking mechanism in objective-c through GCD?
编辑:回来补充一些关于你的“理解”的想法:
串行队列 - 一次只生成一个线程,一旦该线程被释放,它就会被占用或空闲来执行其他任务
串行队列不会“生成”一个线程。队列和线程是不同的东西,具有不同的语义。串行队列需要一个线程来执行工作项,但串行队列和线程之间没有一对一的关系。线程是相对“重”的资源,队列是相对“轻”的资源。单个串行队列在其生命周期内可以在多个线程上执行工作项(尽管同时不会超过一个线程)。 GCD 维护用于执行工作项的线程池,但这是一个实现细节,没有必要了解它是如何实现的以便正确使用队列。
串行队列调度同步 - 阻塞调度串行队列的调用者线程并在该线程上执行任务。
队列(串行或并发)未“分派”(同步或其他方式)。一个工作项被排入队列。该工作项随后将由任意线程执行,很可能包括调用线程。保证一次只有一个排入给定 serial 队列的工作项将被执行(在任何线程上)。
串行队列调度异步 - 不会阻塞 ~caller~ 入队线程,实际上它在不同的线程上运行并保持调用者线程运行。 (为了便于阅读,进行了一些小改动)
这很接近,但不太准确。确实,使用async
将工作项排入串行队列不会阻塞入队线程。工作项不一定是由与入队线程不同的线程执行的,尽管在常见情况下,通常是这种情况。
这里要知道的是,sync
和 async
之间的区别严格限于 enqueueing 线程的行为,并且对哪个线程没有(保证)影响或影响。工作项被执行。如果您使用sync
将工作项入队,入队线程将等待(可能永远,在您在此处概述的特定情况下)等待工作项完成,而如果您使用async
将工作项入队,入队线程将继续正在执行。
【讨论】:
【参考方案2】:正如您所指出的,sync
调用会阻塞当前线程,直到该块运行为止。因此,当您将 sync
发送到您当前所在的同一个串行队列时,您正在阻塞队列,等待一个块在您刚刚阻塞的同一队列上运行,从而导致死锁。
如果你真的想在当前队列上同步运行一些东西,根本不要用sync
调度它,直接运行它。例如:
serialQ.async
print(1)
serialQ.async
print(2)
// serialQ.sync // don't dispatch synchronously to the current serial queue
print(3)
//
或者异步调度。例如,
serialQ.async
print(1)
serialQ.async
print(2)
serialQ.async
print(3)
或者使用并发队列(在这种情况下,您必须小心确保没有线程爆炸,这也可能导致死锁)。例如,
let concurrentQ = DispatchQueue(label: "...", attributes: .concurrent)
concurrentQ.async
print(1)
concurrentQ.async
print(2)
concurrentQ.sync
print(3)
【讨论】:
以上是关于GCD 串行队列调度异步和同步的主要内容,如果未能解决你的问题,请参考以下文章
GCD使用 串行并行队列 与 同步异步执行的各种组合 及要点分析
同步,异步,串行队列,并发队列,全局队列,主队列等概念的总结