队列何时会认为任务已完成



在下面的代码中,queueT(串行队列)会认为"任务A"何时完成
aNetworkRequest切换到另一个线程的时刻
还是在doneInAnotherQueue块中?(评论//1)

换句话说,"任务B"什么时候执行?

let queueT = DispatchQueue(label: "com.test.a")
queueT.async { // task A
aNetworkRequest.doneInAnotherQueue() { // completed in another thread possibly
// 1
}
}
queueT.async { // task B
print("It's my turn") 
} 

如果您能解释队列如何考虑任务完成的机制,那会更好
提前谢谢。

简而言之,第一个示例启动异步网络请求,因此一旦提交该网络请求,async调用就会"完成"(但不等待该网络请求完成)。

我假设真正的问题是你想知道网络请求何时完成。总而言之,GCD不太适合管理任务之间的依赖关系,这些任务本身就是异步请求。将网络请求的启动调度到串行队列无疑不会达到您想要的效果。(在有人建议使用信号量或向wait发送组来完成异步请求之前,请注意,这可以解决战术问题,但这是一种需要避免的模式,因为它对资源的使用效率很低,在边缘情况下,可能会导致死锁。)

一种模式是使用完成处理程序:

func performRequestA(completion: @escaping () -> Void) { // task A
aNetworkRequest.doneInAnotherQueue() { object in
...
completion()
}
}

现在,在实践中,我们通常会使用带有参数的完成处理程序,甚至可能是Result类型:

func performRequestA(completion: @escaping (Result<Foo, Error>) -> Void) { // task A
aNetworkRequest.doneInAnotherQueue() { result in
guard ... else {
completion(.failure(error))
return
}
let foo = ...
completion(.success(foo))
}
}

然后,您可以使用完成处理程序模式来处理结果、更新模型,并可能启动依赖于此请求结果的后续请求。例如:

performRequestA { result in
switch result {
case .failure(let error):
print(error)
case .success(let foo): 
// update models or initiate next step in the process here
}
}

如果你真的在问如何管理异步任务之间的依赖关系,还有很多其他优雅的模式(例如,Combine、自定义异步Operation子类、SE-0296和SE-0303中即将推出的异步/等待模式等)。所有这些都是管理异步任务间依赖关系、控制并发程度的优雅解决方案,等

在我们提出任何具体建议之前,我们可能需要更好地了解您更广泛需求的性质。你已经问过关于一次派遣的问题,但最好从你试图实现的更广泛的背景来看待这个问题。例如,我假设您在问,因为您有多个异步请求要启动:您真的需要确保它们按顺序发生并失去并发的所有性能优势吗?或者,您可以允许它们并发运行吗?您只需要知道所有并发请求何时完成,以及如何以正确的顺序获得结果?您可能有太多并发请求,以至于可能需要限制并发程度?

这些问题的答案可能会影响我们对如何最好地管理多个异步请求的建议。但答案几乎可以肯定不是GCD队列。

您可以进行简单的检查

let queueT = DispatchQueue(label: "com.test.a")
queueT.async { // task A

DispatchQueue(label: "com.test2.a").async { // create another queue inside 
for i in 0..<6 {
print(i)
}
}

}
queueT.async { // task B
for i in 10..<20 {
print(i)
}
}

}

每次运行都会得到不同的输出,这意味着当你切换线程时,任务被认为完成了

当您传递的闭包返回时,GCD工作项就完成了。因此,对于您的示例,我将重写它,使函数调用和参数更加明确(而不是使用尾随闭包语法)。

queueT.async(execute: {
// This is a function call that takes a closure parameter. Whether this
// function returns, then this closure will continue. Whether that is before or
// after running completionHandler is an internal detail of doneInAnotherQueue.
aNetworkRequest.doneInAnotherQueue(closureParameter: { ... })
// At this point, the closure is complete. What doneInAnotherQueue() does with
// its closure is its business.
})

假设CCD_ 8执行其闭包参数"0";"将来某个时候";,那么你的任务B可能会在闭包运行之前运行(可能不会;在这一点上这确实是一场竞赛,但很可能)。如果doneInAnotherQueue()在返回之前阻塞了它的闭包,那么closureParameter肯定会在任务B之前运行。

这里绝对没有魔法。系统不知道doneInAnotherQueue对其参数做了什么。它可能永远不会运行它。它可能会立即运行它。它可能在未来某个时候运行它。系统只是调用doneInAnotherQueue()并将其传递给一个闭包。

我重写了CCD_ 13;具有参数的函数";语法,以便更清楚地表明async()只是一个函数,它需要一个闭包参数。它也不是魔法。这不是语言的一部分。这只是Dispatch框架中的一个正常功能。它所做的一切都是获取参数,将其放在调度队列中,然后返回。它不执行任何操作。只有一些闭包会被放入队列、安排和执行。

Swift正在添加结构化并发,这将添加更多的语言级并发特性,使您能够表达比GCD提供的简单原语更高级的东西。

您的任务A立即返回。将工作调度到另一个队列是同步的。将"doneInAotherQueue"后面的块(尾部闭包)视为doneInAtherQueue函数的一个参数,与传递Int或String没有什么不同。你穿过那个街区,然后带着任务A的大括号立即返回。

最新更新