串行队列/专用调度队列如何知道任务何时完成?

(或许通过串行调度队列如何保证资源保护来回答?但我不明白如何)

gcd如何知道异步任务(例如网络任务)何时完成? 我应该为此目的使用dispatch_retaindispatch_release吗? 更新:我不能用ARC调用这些方法中的任何一个……怎么办?

细节

我正在与进行大量网络访问的第三方库进行交互。 我通过一个小类创建了一个包装器,基本上提供了我需要的第三方类的所有方法,但是将调用包装在dispatch_async(serialQueue) { () -> Void in (其中serialQueue是我的包装类的成员) 。

我试图确保每个对底层库的调用在下一次开始之前完成(某种程度上,它尚未在库中实现)。

串行调度队列上的工作序列化是在直接提交给队列的工作单元上。 一旦执行到达提交的闭包的末尾(或它返回),则可以执行队列上的下一个工作单元。

重要的是,闭包可能已经启动的任何其他异步任务可能仍在运行(或者甚至可能尚未开始运行),但不考虑它们。

例如,对于以下代码:

 dispatch_async(serialQueue) { print("Start") dispatch_async(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 1st") } dispatch_async(serialQueue) { print("Start") dispatch_async(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 2nd") } 

输出将是这样的:

开始

完成第1

开始

完成第二

10秒后

10秒后

请注意,在调度第二个串行任务之前,前10秒任务尚未完成。 现在,比较:

 dispatch_async(serialQueue) { print("Start") dispatch_sync(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 1st") } dispatch_async(serialQueue) { print("Start") dispatch_sync(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 2nd") } 

输出将是这样的:

开始

10秒后

完成第1

开始

10秒后

完成第二

请注意,这次是因为同步调度了10秒的任务,所以串行队列被阻塞,第二个任务在第一个任务完成之前没有启动。

在您的情况下,您正在包装的操作很可能会自己调度异步任务(因为这是网络操作的本质),因此串行调度队列本身是不够的。

您可以使用DispatchGroup来阻止串行调度队列。

 dispatch_async(serialQueue) { let dg = dispatch_group_create() dispatch_group_enter(dg) print("Start") dispatch_async(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") dispatch_group_leave(dg) } dispatch_group_wait(dg) print("Done") } 

这将输出

开始

10秒后

完成

dg.wait()阻塞串行队列,直到dg.leave调用的数量与dg.enter调用的数量相匹配。 如果您使用此技术,则需要注意确保包装操作的所有可能完成路径都调用dg.leavedg.wait()上还有一些变量,它们采用超时参数。

如前所述, DispatchGroup是一个非常好的机制。

您可以将它用于同步任务:

 let group = DispatchGroup() DispatchQueue.global().async(group: group) { syncTask() } group.notify(queue: .main) { // done } 

最好使用notify不是wait ,因为wait会阻塞当前线程,因此在非主线程上是安全的。

您还可以使用它来执行异步任务:

 let group = DispatchGroup() group.enter() asyncTask { group.leave() } group.notify(queue: .main) { // done } 

或者您甚至可以执行任何同步的任意数量的并行任务:

 let group = DispatchGroup() group.enter() asyncTask1 { group.leave() } group.enter() //other way of doing a task with synchronous API DispatchQueue.global().async { syncTask1() group.leave() } group.enter() asyncTask2 { group.leave() } DispatchQueue.global().async(group: group) { syncTask2() } group.notify(queue: .main) { // runs when all tasks are done } 

重要的是要注意一些事情。

  1. 总是检查你的异步函数是否调用完成回调,有时候第三方库会忘记这一点,或者当你self weak情况下,没有人费心去检查当selfnil是否对身体进行了评估。 如果您不检查它,那么您可能会挂起并且永远不会收到通知回调。
  2. 在调用group.notify之前,请记住执行所有必需的group.enter()group.async(group: group)调用。 否则,您可以获得竞争条件,并且在实际完成任务之前, group.notify块可以触发。

不好的例子

 let group = DispatchGroup() DispatchQueue.global().async { group.enter() syncTask1() group.leave() } group.notify(queue: .main) { // Can run before syncTask1 completes - DON'T DO THIS } 

你问题机构中问题的答案:

我试图确保每个对底层库的调用在下一个开始之前完成

串行队列确保按照您将任务添加到队列的顺序进行任务。


我不太明白标题中的问题:

串行队列如何知道任务何时完成?